View Javadoc
1   /*
2    * #%L
3    * wcm.io
4    * %%
5    * Copyright (C) 2015 wcm.io
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  package io.wcm.caravan.hal.docs.impl;
21  
22  import java.io.FileNotFoundException;
23  import java.io.IOException;
24  import java.net.URL;
25  import java.util.Map;
26  
27  import org.apache.felix.scr.annotations.Activate;
28  import org.apache.felix.scr.annotations.Component;
29  import org.apache.felix.scr.annotations.Service;
30  import org.osgi.framework.Bundle;
31  import org.osgi.service.component.ComponentContext;
32  
33  import com.github.jknack.handlebars.Handlebars;
34  import com.github.jknack.handlebars.Template;
35  import com.github.jknack.handlebars.io.AbstractTemplateLoader;
36  import com.github.jknack.handlebars.io.TemplateSource;
37  import com.github.jknack.handlebars.io.URLTemplateSource;
38  import com.google.common.collect.ImmutableMap;
39  
40  import io.wcm.caravan.hal.docs.impl.model.LinkRelation;
41  
42  /**
43   * Renders HTML views for HAL documentation.
44   */
45  @Component(immediate = true)
46  @Service(TemplateRenderer.class)
47  public class TemplateRenderer {
48  
49    private static final String CLASSPATH_TEMPLATES = "HALDOCS-TEMPLATE-INF/templates";
50  
51    private Handlebars handlebars;
52    private Template serviceTemplate;
53    private Template linkRelationTemplate;
54  
55    @Activate
56    void activate(ComponentContext componentContext) throws IOException {
57      Bundle bundle = componentContext.getBundleContext().getBundle();
58      this.handlebars = new Handlebars(new BundleTemplateLoader(bundle));
59      this.serviceTemplate = handlebars.compile("service.html.hbs");
60      this.linkRelationTemplate = handlebars.compile("linkRelation.html.hbs");
61    }
62  
63    /**
64     * Generate HTML file for service.
65     * @param service Service
66     * @return Rendered markup
67     * @throws IOException
68     */
69    public String renderServiceHtml(io.wcm.caravan.hal.docs.impl.model.Service service) throws IOException {
70      Map<String, Object> model = ImmutableMap.<String, Object>builder()
71          .put("service", service)
72          .build();
73      return render(service, model, serviceTemplate);
74    }
75  
76    /**
77     * Generate HTML file for link relation.
78     * @param service Service
79     * @param linkRelation Link relation
80     * @return Rendered markup
81     * @throws IOException
82     */
83    public String renderLinkRelationHtml(io.wcm.caravan.hal.docs.impl.model.Service service, LinkRelation linkRelation)
84        throws IOException {
85      Map<String, Object> model = ImmutableMap.<String, Object>builder()
86          .put("service", service)
87          .put("linkRelation", linkRelation)
88          .build();
89      return render(service, model, linkRelationTemplate);
90    }
91  
92    /**
93     * Generate templated file with handlebars
94     * @param model Model
95     * @param template Template
96     * @return Rendered markup
97     */
98    private String render(io.wcm.caravan.hal.docs.impl.model.Service service, Map<String, Object> model,
99        Template template) throws IOException {
100     Map<String, Object> mergedModel = ImmutableMap.<String, Object>builder()
101         .putAll(model)
102         .put("docsContext", ImmutableMap.<String, Object>builder()
103             .put("baseUrl", DocsPath.get(service.getServiceId()) + "/")
104             .put("resourcesPath", HalDocsBundleTracker.DOCS_RESOURCES_URI_PREFIX)
105             .build())
106         .build();
107     return template.apply(mergedModel);
108   }
109 
110   /**
111    * Loads Handlebars templates from bundle.
112    */
113   private static class BundleTemplateLoader extends AbstractTemplateLoader {
114 
115     private final Bundle bundle;
116 
117     BundleTemplateLoader(Bundle bundle) {
118       this.bundle = bundle;
119     }
120 
121     @Override
122     public TemplateSource sourceAt(String location) throws IOException {
123       URL resource = bundle.getResource(CLASSPATH_TEMPLATES + "/" + location);
124       if (resource == null) {
125         throw new FileNotFoundException(location);
126       }
127       return new URLTemplateSource(location, resource);
128     }
129 
130   }
131 
132 }