001: /* Copyright 2001 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal;
007:
008: import java.io.BufferedReader;
009: import java.io.IOException;
010: import java.io.InputStream;
011: import java.io.InputStreamReader;
012: import java.net.URL;
013: import java.util.Enumeration;
014: import java.util.Hashtable;
015: import java.util.StringTokenizer;
016: import java.util.Vector;
017:
018: import javax.servlet.http.HttpServletRequest;
019: import javax.xml.transform.Source;
020: import javax.xml.transform.stream.StreamSource;
021:
022: import org.apache.commons.logging.Log;
023: import org.apache.commons.logging.LogFactory;
024: import org.jasig.portal.utils.ResourceLoader;
025: import org.jasig.portal.utils.SAX2FilterImpl;
026: import org.xml.sax.ContentHandler;
027: import org.xml.sax.SAXException;
028: import org.xml.sax.XMLReader;
029: import org.xml.sax.helpers.XMLReaderFactory;
030:
031: /**
032: * A tool for managing a collection of stylesheets.
033: * StylesheetSet allows you to instansiate a list
034: * of stylesheets in memory and select one according
035: * to the request/title/media parameters.
036: * @author Peter Kharchenko
037: * @version $Revision: 35418 $
038: */
039: public class StylesheetSet extends SAX2FilterImpl {
040:
041: private static final Log log = LogFactory
042: .getLog(StylesheetSet.class);
043:
044: // Default URI for the media properties file
045: protected static final String m_defaultMediaPropsUri = "/properties/media.properties";
046: protected static Hashtable m_mediaPropsCache = new Hashtable(5);
047: protected String m_myMediaPropsUri = m_defaultMediaPropsUri;
048: protected Hashtable title_table;
049:
050: public StylesheetSet() {
051: title_table = new Hashtable();
052: }
053:
054: /**
055: * Create a SAX filter that will pick up stylesheet bindings in a document that's processed through this filter.
056: *
057: * @param dt a <code>ContentHandler</code> of the downstream SAX listener..
058: */
059: public StylesheetSet(ContentHandler dt) {
060: super (dt);
061: }
062:
063: /**
064: * Creates a new <code>StylesheetSet</code> instance given a .ssl file URI.
065: *
066: * @param uri a <code>String</code> value
067: * @exception PortalException if an error occurs
068: */
069: public StylesheetSet(String uri) throws PortalException {
070: try {
071: XMLReader reader = XMLReaderFactory.createXMLReader();
072: StylesheetSet dummy = new StylesheetSet();
073: reader.setContentHandler((ContentHandler) dummy);
074: URL url = null;
075: try {
076: url = new URL(uri);
077: reader.parse(url.toString());
078: this .title_table = dummy.getTitleTable();
079: } catch (IOException ioe) {
080: throw new ResourceMissingException(
081: url.toString(),
082: "XSLT stylesheet",
083: "StylesheetSet(uri) : Unable to read stylesheet set from the specified location. Please check the URL.");
084: } catch (SAXException se) {
085: throw new GeneralRenderingException(
086: "StylesheetSet(uri) : Unable to parse stylesheet set (.ssl) file. URL=\""
087: + url + "\"", se);
088: }
089: } catch (SAXException se) {
090: // Log the exception
091: log.error("Error constructing StylesheetSet from uri["
092: + uri + "]", se);
093: throw new GeneralRenderingException(
094: "StylesheetSet(uri) : Unable to instantiate SAX Reader. Please check your library installation.");
095: }
096: }
097:
098: /**
099: * Obtain a stylesheet transform source
100: *
101: * @param title a <code>String</code> value
102: * @return a <code>Source</code> for a given stylesheet
103: */
104: public Source getStylesheet(String title) {
105: Hashtable media_table = (Hashtable) title_table.get(title);
106: if (media_table == null) {
107: return null;
108: }
109: StylesheetDescription sd = null;
110: if (media_table.isEmpty()) {
111: return null;
112: }
113: for (Enumeration e = media_table.elements(); e
114: .hasMoreElements();) {
115: if (sd == null) {
116: sd = (StylesheetDescription) e.nextElement();
117: } else {
118: StylesheetDescription tsd = (StylesheetDescription) e
119: .nextElement();
120: if (!tsd.getAlternate())
121: sd = tsd;
122: }
123: }
124: // after all this mess we should have a valid sd
125: return (new StreamSource(sd.getURI()));
126: }
127:
128: /**
129: * Obtains a default stylesheet.
130: *
131: * @return a <code>Source</code> for a default stylesheet.
132: */
133: public Source getStylesheet() {
134: // this is painful ... browse through all possible
135: // browse through all titles to find a non-alternate
136: // stylesheet
137: StylesheetDescription sd = null;
138: for (Enumeration e = title_table.elements(); e
139: .hasMoreElements();) {
140: Hashtable media_table = (Hashtable) e.nextElement();
141: if (!media_table.isEmpty()) {
142: for (Enumeration f = media_table.elements(); f
143: .hasMoreElements();) {
144: if (sd == null) {
145: sd = (StylesheetDescription) f.nextElement();
146: } else {
147: StylesheetDescription tsd = (StylesheetDescription) f
148: .nextElement();
149: if (!tsd.getAlternate()) {
150: sd = tsd;
151: }
152: }
153: if (!sd.getAlternate()) {
154: break;
155: }
156: }
157: }
158: }
159: return (new StreamSource(sd.getURI()));
160: }
161:
162: /**
163: * Obtain a stylesheet.
164: *
165: * @param title stylesheet title
166: * @param media stylesheet media
167: * @return a <code>Source</code> for the stylesheet.
168: */
169: public Source getStylesheet(String title, String media) {
170: Hashtable media_table = (Hashtable) title_table.get(title);
171: if (media_table == null) {
172: return null;
173: }
174: StylesheetDescription sd = (StylesheetDescription) media_table
175: .get(media);
176: if (sd == null) {
177: Enumeration sls = media_table.elements();
178: if (sls.hasMoreElements()) {
179: sd = (StylesheetDescription) sls.nextElement();
180: }
181: }
182: if (sd == null) {
183: return null;
184: }
185: return (new StreamSource(sd.getURI()));
186: }
187:
188: /**
189: * Obtain a stylesheet
190: *
191: * @param title stylesheet title
192: * @param bi current <code>BrowserInfo</code> value
193: * @return a <code>Source</code> for the stylesheet
194: * @exception PortalException if an error occurs
195: */
196: public Source getStylesheet(String title, BrowserInfo bi)
197: throws PortalException {
198: String media = getMedia(bi);
199: Hashtable media_table = (Hashtable) title_table.get(title);
200: if (media_table == null) {
201: return null;
202: }
203:
204: StylesheetDescription sd = (StylesheetDescription) media_table
205: .get(media);
206: if (sd == null) {
207: Enumeration sls = media_table.elements();
208: if (sls.hasMoreElements())
209: sd = (StylesheetDescription) sls.nextElement();
210: }
211: if (sd == null)
212: return null;
213: return (new StreamSource(sd.getURI()));
214: }
215:
216: /**
217: * Returns the URI of the stylesheet matching the media
218: * @param media
219: * @return the stylesheet URI
220: */
221: public String getStylesheetURI(String media)
222: throws GeneralRenderingException {
223: if (media == null) {
224: throw (new GeneralRenderingException(
225: "StylesheetSet.getStylesheetURI(): Media argument cannot be null"));
226: }
227: String ssURI = null;
228: StylesheetDescription sd = getStylesheetDescription(media);
229: if (sd != null) {
230: ssURI = sd.getURI();
231: }
232: return ssURI;
233: }
234:
235: /**
236: * Obtain a matching stylesheet.
237: *
238: * @param req current request value.
239: * @return a <code>String</code> stylesheet URI
240: * @exception PortalException if an error occurs
241: */
242: public String getStylesheetURI(HttpServletRequest req)
243: throws PortalException {
244: return (getStylesheetURI(getMedia(req)));
245: }
246:
247: /**
248: * Obtain a matching stylesheet URI
249: *
250: * @param bi a <code>BrowserInfo</code> value
251: * @return a <code>String</code> styleshet
252: * @exception PortalException if an error occurs
253: */
254: public String getStylesheetURI(BrowserInfo bi)
255: throws PortalException {
256: return getStylesheetURI(getMedia(bi));
257: }
258:
259: /**
260: * Obtain stylesheet URI
261: *
262: * @param title stylesheet title
263: * @param req a <code>HttpServletRequest</code> value
264: * @return a <code>String</code> stylesheet URI
265: * @exception PortalException if an error occurs
266: */
267: public String getStylesheetURI(String title, HttpServletRequest req)
268: throws PortalException {
269: return getStylesheetURI(title, getMedia(req));
270: }
271:
272: /**
273: * Describe <code>getStylesheetURI</code> method here.
274: *
275: * @param title a stylesheet title
276: * @param bi a <code>BrowserInfo</code> value
277: * @return a <code>String</code> stylesheet URI
278: * @exception PortalException if an error occurs
279: */
280: public String getStylesheetURI(String title, BrowserInfo bi)
281: throws PortalException {
282: return getStylesheetURI(title, getMedia(bi));
283: }
284:
285: /**
286: * Obtain a stylesheet URI
287: *
288: * @param title stylesheet title
289: * @param media media value
290: * @return a <code>String</code> stylesheet URI
291: * @exception GeneralRenderingException if an error occurs
292: */
293: public String getStylesheetURI(String title, String media)
294: throws GeneralRenderingException {
295: if (title != null) {
296: Hashtable media_table = (Hashtable) title_table.get(title);
297: if (media_table == null) {
298: return null;
299: }
300: if (log.isDebugEnabled())
301: log.debug("media=\"" + media + "\"");
302: StylesheetDescription sd = (StylesheetDescription) media_table
303: .get(media);
304: if (sd == null) {
305: Enumeration sls = media_table.elements();
306: if (sls.hasMoreElements()) {
307: sd = (StylesheetDescription) sls.nextElement();
308: }
309: }
310: if (sd == null) {
311: return null;
312: }
313: return sd.getURI();
314: } else {
315: return getStylesheetURI(media);
316: }
317: }
318:
319: protected StylesheetDescription getStylesheetDescription(
320: String media) throws GeneralRenderingException {
321: if (media == null) {
322: log
323: .error("StylesheetSet::getStylesheetDescription() : media argument is null");
324: throw (new GeneralRenderingException(
325: "StylesheetSet.getStylesheetDescription(): Null media argument passed in"));
326: }
327: // search for a non-alternate stylesheet for a particular media
328: StylesheetDescription sd = null;
329: for (Enumeration e = title_table.elements(); e
330: .hasMoreElements();) {
331: Hashtable media_table = (Hashtable) e.nextElement();
332: StylesheetDescription tsd = (StylesheetDescription) media_table
333: .get(media);
334: if (tsd != null) {
335: if (sd == null) {
336: sd = tsd;
337: }
338: if (!tsd.getAlternate()) {
339: sd = tsd;
340: break;
341: }
342: } else {
343: Enumeration sls = media_table.elements();
344: if (sls.hasMoreElements()) {
345: sd = (StylesheetDescription) sls.nextElement();
346: }
347: }
348: }
349: return sd;
350: }
351:
352: /**
353: * Obtain a stylesheet source.
354: *
355: * @param title stylesheet title
356: * @param req current request
357: * @return a <code>Source</code> for the stylesheet.
358: * @exception PortalException if an error occurs
359: */
360: public Source getStylesheet(String title, HttpServletRequest req)
361: throws PortalException {
362: // log.debug("getStylesheet(title,req) : Looking up the media name for "+req.getHeader("User-Agent")+" : media=\""+getMedia(req)+"\"");
363: return getStylesheet(title, getMedia(req));
364: }
365:
366: /**
367: * Obtain a stylesheet source.
368: *
369: * @param req an <code>HttpServletRequest</code> value
370: * @return a <code>Source</code> for the stylesheet
371: * @exception PortalException if an error occurs
372: */
373: public Source getStylesheet(HttpServletRequest req)
374: throws PortalException {
375: StylesheetDescription sd = getStylesheetDescription(getMedia(req));
376: if (sd != null) {
377: return new StreamSource(sd.getURI());
378: } else {
379: return null;
380: }
381: }
382:
383: /**
384: * Obtain a stylesheet for a given media.
385: *
386: * @param media desired media
387: * @return a <code>Source</code> for the stylesheet.
388: * @exception GeneralRenderingException if an error occurs
389: */
390: public Source getStylesheetByMedia(String media)
391: throws GeneralRenderingException {
392: // log.debug("getStylesheet(req) : Looking up the media name for "+req.getHeader("User-Agent")+" : media=\""+getMedia(req)+"\"");
393: StylesheetDescription sd = getStylesheetDescription(media);
394: if (sd != null) {
395: return new StreamSource(sd.getURI());
396: } else {
397: return (null);
398: }
399: }
400:
401: /**
402: * Add a stylesheet to the list.
403: *
404: * @param sd a <code>StylesheetDescription</code> value
405: */
406: public void addStyleSheet(StylesheetDescription sd) {
407: // see if the title is already in the hashtable
408: Hashtable media_table = (Hashtable) title_table.get(sd
409: .getTitle());
410: if (media_table == null) {
411: media_table = new Hashtable();
412: media_table.put(sd.getMedia(), sd);
413: title_table.put(sd.getTitle(), media_table);
414: } else {
415: media_table.put(sd.getMedia(), sd);
416: }
417: }
418:
419: /**
420: * Fills StylesheetSet by accepting SAX events
421: * @param target
422: * @param data
423: * @exception SAXException
424: */
425: public void processingInstruction(String target, String data)
426: throws SAXException {
427: if (target.equals("xml-stylesheet")) {
428: StylesheetDescription sd = new StylesheetDescription(data);
429: this .addStyleSheet(sd);
430: }
431: // pass on the stylesheet instruction
432: if (this .getContentHandler() != null) {
433: this .getContentHandler()
434: .processingInstruction(target, data);
435: }
436: }
437:
438: protected OrderedProps getMediaProps() throws PortalException {
439: // Check to see if the media properties are in the cache
440: if (m_mediaPropsCache.containsKey(m_myMediaPropsUri)) {
441: return ((OrderedProps) m_mediaPropsCache
442: .get(m_myMediaPropsUri));
443: } else {
444: // Try to load the media properties
445: setMediaProps(m_myMediaPropsUri);
446: }
447:
448: // Try to return them from the cache again
449: return ((OrderedProps) m_mediaPropsCache.get(m_myMediaPropsUri));
450: }
451:
452: /**
453: * Set the location of the media properties object.
454: *
455: * @param uri a <code>String</code> value
456: * @exception PortalException if an error occurs
457: */
458: public void setMediaProps(String uri) throws PortalException {
459: if (uri == null) {
460: // Use the default URI
461: uri = m_defaultMediaPropsUri;
462: } else {
463: // Fix up the provided URI
464: uri = ResourceLoader.getResourceAsURLString(
465: this .getClass(), uri);
466:
467: // Cache the URI of the media props that this instance will use
468: m_myMediaPropsUri = uri;
469: }
470:
471: // Check to see if we've already cached these properties
472: if (m_mediaPropsCache.containsKey(uri)) {
473: return;
474: }
475:
476: try {
477: // Create a URL from the given URI
478: URL url = new URL(uri);
479: if (url != null) {
480: // Put the loaded media properties in the cache
481: InputStream in = url.openStream();
482: try {
483: m_mediaPropsCache.put(uri, new OrderedProps(in));
484: } finally {
485: in.close();
486: }
487: } else {
488: throw new ResourceMissingException(uri,
489: "The media.properties file",
490: "Unable to understand the media.properties URI");
491: }
492: } catch (IOException ioe) {
493: throw new ResourceMissingException(uri,
494: "The media.properties file ", ioe);
495: }
496: }
497:
498: protected Hashtable getTitleTable() {
499: return title_table;
500: }
501:
502: protected String getMedia(HttpServletRequest req)
503: throws PortalException {
504: String ua = req.getHeader("User-Agent");
505: if (ua == null || ua.equals("")) {
506: ua = MediaManager.NULL_USER_AGENT;
507: }
508:
509: return (getMediaProps().getValue(ua));
510: }
511:
512: protected String getMedia(BrowserInfo bi) throws PortalException {
513: return (getMediaProps().getValue(bi.getUserAgent()));
514: }
515:
516: /**
517: * COPIED FROM XALAN SOURCE
518: * Stores the keys and values from a file (similar to a properties file) and
519: * can return the first value which has a key contained in its string.
520: * File can have comment lines starting with '#" and for each line the entries are
521: * separated by tabs and '=' char.
522: */
523: class OrderedProps {
524: /**
525: * Stores the Key and Values as an array of Strings
526: */
527: private Vector attVec = new Vector(15);
528:
529: /**
530: * Constructor.
531: * @param inputStream Stream containing the properties file.
532: * @exception IOException Thrown if unable to read from stream
533: */
534: OrderedProps(InputStream inputStream) throws IOException {
535: BufferedReader input = new BufferedReader(
536: new InputStreamReader(inputStream));
537: String currentLine, Key = null;
538: StringTokenizer currentTokens;
539: while ((currentLine = input.readLine()) != null) {
540: currentTokens = new StringTokenizer(currentLine,
541: "=\t\r\n");
542: if (currentTokens.hasMoreTokens()) {
543: Key = currentTokens.nextToken().trim();
544: }
545:
546: if ((Key != null) && !Key.startsWith("#")
547: && currentTokens.hasMoreTokens()) {
548: String temp[] = new String[2];
549: temp[0] = Key;
550: temp[1] = currentTokens.nextToken().trim();
551: attVec.addElement(temp);
552: }
553: }
554: input.close();
555: }
556:
557: /**
558: * Iterates through the Key list and returns the first value for whose
559: * key the given string contains. Returns "unknown" if no key is contained
560: * in the string.
561: * @param s String being searched for a key.
562: * @return Value for key found in string, otherwise "unknown"
563: */
564: String getValue(String s) {
565: if (s == null) {
566: return null;
567: }
568: int i, j = attVec.size();
569: for (i = 0; i < j; i++) {
570: String temp[] = (String[]) attVec.elementAt(i);
571: if (s.indexOf(temp[0]) > -1) {
572: return temp[1];
573: }
574: }
575: return "unknown";
576: }
577: }
578: }
|