001: /*
002: * hgcommons 7
003: * Hammurapi Group Common Library
004: * Copyright (C) 2003 Hammurapi Group
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.hammurapi.biz/hammurapi-biz/ef/xmenu/hammurapi-group/products/products/hgcommons/index.html
021: * e-Mail: support@hammurapi.biz
022: */
023: package biz.hammurapi.util;
024:
025: import java.io.IOException;
026: import java.io.InputStream;
027: import java.io.InputStreamReader;
028: import java.util.AbstractCollection;
029: import java.util.Collection;
030: import java.util.HashMap;
031: import java.util.Iterator;
032: import java.util.Locale;
033: import java.util.Map;
034: import java.util.Properties;
035:
036: import javax.xml.parsers.DocumentBuilderFactory;
037: import javax.xml.parsers.FactoryConfigurationError;
038: import javax.xml.parsers.ParserConfigurationException;
039: import javax.xml.transform.Result;
040: import javax.xml.transform.Source;
041: import javax.xml.transform.Templates;
042: import javax.xml.transform.Transformer;
043: import javax.xml.transform.TransformerConfigurationException;
044: import javax.xml.transform.TransformerException;
045: import javax.xml.transform.TransformerFactory;
046: import javax.xml.transform.TransformerFactoryConfigurationError;
047: import javax.xml.transform.URIResolver;
048: import javax.xml.transform.dom.DOMSource;
049: import javax.xml.transform.stream.StreamSource;
050:
051: import org.w3c.dom.Document;
052: import org.w3c.dom.Element;
053:
054: import biz.hammurapi.config.Context;
055: import biz.hammurapi.eval.ExpandingFilterWriter;
056: import biz.hammurapi.xml.dom.CompositeDomSerializer;
057:
058: /**
059: * Finds class stylesheet and transforms given
060: * object using the stylesheet.
061: * @author Pavel Vlasov
062: * @revision $Revision$
063: */
064: public class ClassTransformerFactory {
065: private static class Key {
066: String className;
067: String profile;
068: Locale locale;
069:
070: public boolean equals(Object otherTemplateKey) {
071: if (this == otherTemplateKey) {
072: return true;
073: }
074:
075: if (otherTemplateKey instanceof Key) {
076: Key otk = (Key) otherTemplateKey;
077: return className.equals(otk.className)
078: && (profile == null ? otk.profile == null
079: : profile.equals(otk.profile))
080: && (locale == null ? otk.locale == null
081: : locale.equals(otk.locale));
082: }
083:
084: return false;
085: }
086:
087: public int hashCode() {
088: return className.hashCode()
089: ^ (profile == null ? 0 : profile.hashCode())
090: ^ (locale == null ? 0 : locale.hashCode());
091: }
092:
093: /**
094: * @param className
095: * @param profile
096: * @param locale
097: */
098: public Key(String className, String profile, Locale locale) {
099: super ();
100: this .className = className;
101: this .profile = profile;
102: this .locale = locale;
103: }
104: }
105:
106: private Map templates = new HashMap();
107: private Map contexts = new HashMap();
108: private TransformerFactory factory = TransformerFactory
109: .newInstance();
110:
111: private boolean cacheExpandedTemplates;
112: private CompositeDomSerializer domSerializer;
113: private ClassTemplateResolver templateResolver;
114:
115: private Templates findTemplate(Class clazz, String profile,
116: Locale locale, Context context)
117: throws TransformerFactoryConfigurationError,
118: TransformerConfigurationException, IOException {
119: if (context == null || cacheExpandedTemplates) {
120: synchronized (templates) {
121: Key key = new Key(clazz.getName(), profile, locale);
122: Templates ret = (Templates) templates.get(key);
123: if (ret == null && !templates.containsKey(key)) {
124: ret = loadTemplate(clazz, profile, locale, context);
125: templates.put(key, ret);
126: }
127:
128: return ret;
129: }
130: }
131: return loadTemplate(clazz, profile, locale, context);
132: }
133:
134: /**
135: * @param clazz
136: * @param profile
137: * @param locale
138: * @param context
139: * @return
140: * @throws TransformerConfigurationException
141: * @throws IOException
142: */
143: private Templates loadTemplate(Class clazz, String profile,
144: Locale locale, final Context context)
145: throws TransformerConfigurationException, IOException {
146: InputStream is = null;
147: if (templateResolver != null) {
148: is = templateResolver.resolve(clazz, profile, locale);
149: }
150:
151: if (is == null) {
152: is = new ClassResourceLoader(clazz).getResourceAsStream(
153: profile, locale, "xsl");
154: }
155:
156: if (is == null) {
157: return null;
158: }
159: final Context classContext = findContext(clazz, profile, locale);
160: if (classContext == null && context == null) {
161: return factory.newTemplates(new StreamSource(is));
162: }
163: return factory.newTemplates(new StreamSource(
164: ExpandingFilterWriter.expand(new InputStreamReader(is),
165: new Context() {
166:
167: public Object get(String name) {
168: Object ret = context == null ? null
169: : context.get(name);
170: if (ret == null && classContext != null) {
171: ret = classContext.get(name);
172: }
173: return ret;
174: }
175: })));
176: }
177:
178: private Context findContext(Class clazz, String profile,
179: Locale locale) {
180: synchronized (contexts) {
181: Key key = new Key(clazz.getName(), profile, locale);
182: Context ret = (Context) contexts.get(key);
183: if (ret == null && !contexts.containsKey(key)) {
184: ret = new ClassResourceLoader(clazz).getContext(
185: profile, locale, "ctx");
186: contexts.put(key, ret);
187: }
188:
189: return ret;
190: }
191: }
192:
193: /**
194: * Transforms object using object's class stylesheet. Collections are treated differently - for them
195: * a stylesheet of the first element with profile 'list' is used if profile is null. If there are no
196: * elements in the collection or if a stylesheet is not found then ClassTransformerFactory!list stylesheet is used.
197: * @param object
198: * @param profile
199: * @param locale
200: * @param parameters
201: * @param out
202: * @throws TransformerException
203: * @throws FactoryConfigurationError
204: * @throws ParserConfigurationException
205: * @throws TransformerFactoryConfigurationError
206: * @throws IOException
207: * @throws TransformerConfigurationException
208: */
209: public void transform(final Object object, String rootName,
210: String profile, Locale locale, final Context expandContext,
211: Properties parameters, Result out)
212: throws TransformerException, ParserConfigurationException,
213: FactoryConfigurationError,
214: TransformerConfigurationException,
215: TransformerFactoryConfigurationError, IOException {
216:
217: final Class[] clazz = { null };
218: Document doc = DocumentBuilderFactory.newInstance()
219: .newDocumentBuilder().newDocument();
220: Element root = doc.createElement(rootName == null ? "root"
221: : rootName);
222: doc.appendChild(root);
223:
224: final Locale actualLocale = locale == null ? Locale
225: .getDefault() : locale;
226:
227: if (object instanceof Collection) {
228: domSerializer.toDomSerializable(new AbstractCollection() {
229:
230: public int size() {
231: return ((Collection) object).size();
232: }
233:
234: public Iterator iterator() {
235: return new Iterator() {
236: Iterator master = ((Collection) object)
237: .iterator();
238:
239: public void remove() {
240: master.remove();
241: }
242:
243: public boolean hasNext() {
244: return master.hasNext();
245: }
246:
247: public Object next() {
248: Object ret = master.next();
249: if (clazz[0] == null && ret != null) {
250: clazz[0] = ret.getClass();
251: }
252: return ret;
253: }
254: };
255: }
256:
257: }).toDom(root);
258:
259: if (clazz[0] == null) {
260: clazz[0] = ClassTransformerFactory.class;
261: }
262:
263: if (profile == null) {
264: profile = "list";
265: }
266: } else {
267: clazz[0] = object.getClass();
268: domSerializer.toDomSerializable(object).toDom(root);
269: }
270:
271: Templates template = findTemplate(clazz[0], profile,
272: actualLocale, expandContext);
273: if (template == null && object instanceof Collection) {
274: template = findTemplate(ClassTransformerFactory.class,
275: profile, actualLocale, expandContext);
276: }
277:
278: Transformer transformer = template == null ? factory
279: .newTransformer() : template.newTransformer();
280:
281: final URIResolver originalUriResolver = factory
282: .getURIResolver();
283: final String finalProfile = profile;
284: transformer.setURIResolver(new URIResolver() {
285:
286: public Source resolve(String uri, String base)
287: throws TransformerException {
288: String resourceUriPrefix = "resource:";
289: if (uri.equals(resourceUriPrefix + "super")) {
290: final InputStream[] super Resource = { null };
291: final Class[] super Class = { null };
292: new ClassHierarchyVisitable(clazz[0])
293: .accept(new Visitor() {
294:
295: public boolean visit(Object target) {
296: if (target != clazz[0]) {
297: super Class[0] = (Class) target;
298:
299: if (templateResolver != null) {
300: super Resource[0] = templateResolver
301: .resolve(
302: super Class[0],
303: finalProfile,
304: actualLocale);
305: }
306:
307: if (super Resource != null) {
308: return false;
309: }
310:
311: for (int i = 0; i < 4; i++) {
312: String variant = super Class[0]
313: .getName().replace(
314: '.', '/');
315: if (finalProfile != null) {
316: variant += finalProfile;
317: }
318:
319: switch (i) {
320: case 0:
321: variant += actualLocale;
322: break;
323: case 1:
324: variant += "_"
325: + actualLocale
326: .getLanguage();
327: if (actualLocale
328: .getCountry()
329: .length() != 0) {
330: variant += "_"
331: + actualLocale
332: .getCountry();
333: }
334: break;
335: case 2:
336: variant += "_"
337: + actualLocale
338: .getLanguage();
339: break;
340: case 3:
341: break;
342: }
343:
344: variant += ".xsl";
345:
346: super Resource[0] = clazz[0]
347: .getClassLoader()
348: .getResourceAsStream(
349: variant);
350: if (super Resource[0] != null) {
351: return false;
352: }
353: }
354: }
355:
356: return true;
357: }
358:
359: });
360:
361: if (super Resource[0] == null) {
362: throw new TransformerException(
363: "Cannot resolve: " + uri);
364: }
365:
366: try {
367: return new StreamSource(ExpandingFilterWriter
368: .expand(new InputStreamReader(
369: super Resource[0]),
370: new Context() {
371:
372: public Object get(
373: String name) {
374: Object ret = expandContext == null ? null
375: : expandContext
376: .get(name);
377: final Context classContext = findContext(
378: super Class[0],
379: finalProfile,
380: actualLocale);
381: if (ret == null
382: && classContext != null) {
383: ret = classContext
384: .get(name);
385: }
386: return ret;
387: }
388: }));
389: } catch (IOException e) {
390: throw new TransformerException(e);
391: }
392: } else if (uri.startsWith(resourceUriPrefix)) {
393: try {
394: final Class resourceClass = clazz[0]
395: .getClassLoader().loadClass(
396: uri.substring(resourceUriPrefix
397: .length()));
398: ClassResourceLoader crl = new ClassResourceLoader(
399: resourceClass);
400: InputStream is = crl.getResourceAsStream(
401: finalProfile, actualLocale, "xsl");
402: if (is == null) {
403: throw new TransformerException(
404: "Cannot resolve: " + uri);
405: }
406: return new StreamSource(ExpandingFilterWriter
407: .expand(new InputStreamReader(is),
408: new Context() {
409:
410: public Object get(
411: String name) {
412: Object ret = expandContext == null ? null
413: : expandContext
414: .get(name);
415: final Context classContext = findContext(
416: resourceClass,
417: finalProfile,
418: actualLocale);
419: if (ret == null
420: && classContext != null) {
421: ret = classContext
422: .get(name);
423: }
424: return ret;
425: }
426: }));
427:
428: } catch (ClassNotFoundException e) {
429: throw new TransformerException(e);
430: } catch (IOException e) {
431: throw new TransformerException(e);
432: }
433: }
434:
435: return originalUriResolver.resolve(uri, base);
436: }
437:
438: });
439:
440: if (parameters != null) {
441: Iterator it = parameters.entrySet().iterator();
442: while (it.hasNext()) {
443: Map.Entry entry = (Map.Entry) it.next();
444: transformer.setParameter((String) entry.getKey(), entry
445: .getValue());
446: }
447: }
448:
449: transformer.transform(new DOMSource(doc), out);
450: }
451:
452: public ClassTransformerFactory(
453: ClassTemplateResolver templateResolver,
454: CompositeDomSerializer domSerializer,
455: boolean cacheExpandedTemplates) {
456: this.templateResolver = templateResolver;
457: this.domSerializer = domSerializer == null ? CompositeDomSerializer
458: .getThreadInstance()
459: : domSerializer;
460: this.cacheExpandedTemplates = cacheExpandedTemplates;
461: }
462: }
|