001 package com.hammurapi.render;
002
003 import java.io.File;
004 import java.io.FileWriter;
005 import java.io.IOException;
006 import java.io.InputStream;
007 import java.io.Writer;
008 import java.net.URL;
009 import java.util.HashMap;
010 import java.util.Locale;
011 import java.util.Map;
012
013 import org.onemind.commons.java.util.FileUtils;
014 import org.onemind.jxp.CachedJxpPage;
015 import org.onemind.jxp.CachingPageSource;
016 import org.onemind.jxp.JxpContext;
017 import org.onemind.jxp.JxpPageSource;
018 import org.onemind.jxp.JxpProcessor;
019
020 import com.hammurapi.common.ClassHierarchyVisitable;
021 import com.hammurapi.common.Context;
022 import com.hammurapi.common.IdentityManager;
023 import com.hammurapi.common.Visitor;
024 import com.hammurapi.convert.ConvertingService;
025
026 /**
027 * JxpRenderer renders using JXP template engine and pages loaded by classloader.
028 * @author Pavel Vlasov
029 *
030 */
031 public class JxpRenderer implements WriterRenderer, FileRenderer, RenderingConstants {
032
033 /**
034 * Loads rendering resource form toRender class loader.
035 * @author Pavel Vlasov
036 *
037 */
038 private JxpPageSource pageSource = new CachingPageSource() {
039
040 /**
041 * {@inheritDoc}
042 */
043 private String getStreamName(String pageName) {
044 return FileUtils.concatFilePath(DefaultRenderingPageSource.PATH_PREFIX, pageName);
045 }
046
047 /**
048 * {@inheritDoc}
049 */
050
051 protected boolean isExpired(CachedJxpPage page) {
052 return false;
053 }
054
055 /**
056 * {@inheritDoc}
057 */
058 protected boolean hasStream(String pageName) {
059 String streamName = getStreamName(pageName);
060 URL resource = toRender.getClass().getResource(streamName);
061 return resource != null;
062 }
063
064 /**
065 * {@inheritDoc}
066 */
067 protected InputStream loadStream(CachedJxpPage page) throws IOException {
068 return toRender.getClass().getResourceAsStream(getStreamName(page.getName()));
069 }
070 };
071
072 private Object toRender;
073
074 public JxpRenderer(Object obj) {
075 this.toRender = obj;
076 }
077
078 /**
079 * Renders to a file.
080 * This method does not overwrite existing files if they were created after rendering has started.
081 * It prevents infinite loops in rendering.
082 */
083 @Override
084 public boolean render(File out, Map<String, Object> environment, Context context, String profile, Locale locale) throws RenderingException {
085 if (environment!=null) {
086 Object renderStart = environment.get(RENDER_START);
087 if (renderStart instanceof Long) {
088 if (out.exists() && out.isFile() && out.lastModified()>=((Long) renderStart)) {
089 return false;
090 }
091 }
092 }
093
094 try {
095 Writer writer = new FileWriter(out);
096 try {
097 return render(writer, environment, context, profile, locale, out.getParentFile());
098 } finally {
099 writer.close();
100 }
101 } catch (Exception e) {
102 throw new RenderingException(e);
103 }
104 }
105
106 @Override
107 public boolean render(Writer out, Map<String, Object> environment, Context context, final String profile, final Locale locale, File outputDir) throws RenderingException {
108 final JxpPageSource ps = context.lookup(JxpPageSource.class);
109
110 class SearchResult {
111 String pageName;
112 JxpPageSource pageSource;
113
114 SearchResult(String pageName, JxpPageSource pageSource) {
115 super();
116 this.pageName = pageName;
117 this.pageSource = pageSource;
118 }
119 }
120
121 final SearchResult[] sr = {null};
122
123 ClassHierarchyVisitable chv = new ClassHierarchyVisitable(toRender.getClass());
124
125 chv.accept(new Visitor<Class<?>>() {
126
127 Locale actualLocale = locale==null ? Locale.getDefault() : locale;
128
129 @Override
130 public boolean visit(Class<?> target) {
131 for (int i=0; i<4; i++) {
132 String variant=target.getName().replace('.','/');
133
134 if (profile!=null) {
135 variant+="!"+profile;
136 }
137
138 switch (i) {
139 case 0:
140 variant+="_"+actualLocale;
141 break;
142 case 1:
143 variant+="_"+actualLocale.getLanguage();
144 if (actualLocale.getCountry().length()!=0) {
145 variant+="_"+actualLocale.getCountry();
146 }
147 break;
148 case 2:
149 variant+="_"+actualLocale.getLanguage();
150 break;
151 case 3:
152 break;
153 }
154
155 variant+=".jxp";
156
157 if (ps!=null && ps.hasJxpPage(variant)) {
158 sr[0] = new SearchResult(variant, ps);
159 return false;
160 }
161
162 if (pageSource.hasJxpPage(variant)) {
163 sr[0] = new SearchResult(variant, pageSource);
164 return false;
165 }
166 }
167
168 return true;
169 }
170
171 });
172
173 if (sr[0]==null) {
174 return false;
175 }
176
177 JxpContext jxpContext = new JxpContext(sr[0].pageSource);
178 JxpProcessor processor = new JxpProcessor(jxpContext);
179
180 try {
181 Map<String, Object> env = environment==null ? new HashMap<String, Object>() : new HashMap<String, Object>(environment);
182 env.put(TO_RENDER, toRender);
183 // env.remove(TO_RENDER_TYPE_NAME); // Clean up.
184 processor.process(sr[0].pageName, out, env);
185 postRender(env, context, profile, locale, outputDir);
186 return true;
187 } catch (Exception e) {
188 throw new RenderingException(e);
189 }
190 }
191
192 /**
193 * This method is invoked after successful rendering. This implementation is tailored for report generator -
194 * it renders details and contents if outputDir environment entry is instance of File and profile is outline or
195 * outline_http
196 * @param environment
197 * @param context
198 * @param profile
199 * @param locale
200 * @throws RenderingException
201 */
202 protected void postRender(Map<String, Object> environment, Context context, final String profile, final Locale locale, File outputDir) throws RenderingException {
203 if (outputDir!=null && (OUTLINE.equals(profile) || OUTLINE_HTTP.equals(profile))) {
204 IdentityManager<?> identityManager = context.lookup(IdentityManager.class);
205 Object id = identityManager.getIdentity(toRender);
206 File detailsOut = new File(outputDir, "e"+id+".html");
207 FileRenderer fileRenderer = ConvertingService.convert(toRender, FileRenderer.class);
208 if (fileRenderer==null) {
209 render(detailsOut, environment, context, null, locale);
210 } else {
211 fileRenderer.render(detailsOut, environment, context, null, locale);
212 }
213
214 boolean http = OUTLINE_HTTP.equals(profile);
215 File contentsOut = new File(outputDir, "e"+id+"_contents.html");
216 if (fileRenderer==null) {
217 render(contentsOut, environment, context, http ? CONTENTS_HTTP : CONTENTS, locale);
218 } else {
219 fileRenderer.render(contentsOut, environment, context, http ? CONTENTS_HTTP : CONTENTS, locale);
220 }
221 }
222 }
223 }
224