001: /*
002: * List.java
003: *
004: * Version: $Revision: 1.10 $
005: *
006: * Date: $Date: 2006/07/13 23:21:06 $
007: *
008: * Copyright (c) 2002, Hewlett-Packard Company and Massachusetts
009: * Institute of Technology. All rights reserved.
010: *
011: * Redistribution and use in source and binary forms, with or without
012: * modification, are permitted provided that the following conditions are
013: * met:
014: *
015: * - Redistributions of source code must retain the above copyright
016: * notice, this list of conditions and the following disclaimer.
017: *
018: * - Redistributions in binary form must reproduce the above copyright
019: * notice, this list of conditions and the following disclaimer in the
020: * documentation and/or other materials provided with the distribution.
021: *
022: * - Neither the name of the Hewlett-Packard Company nor the name of the
023: * Massachusetts Institute of Technology nor the names of their
024: * contributors may be used to endorse or promote products derived from
025: * this software without specific prior written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
028: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
029: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
030: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
031: * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
032: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
033: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
034: * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
035: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
036: * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
037: * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
038: * DAMAGE.
039: */
040:
041: package org.dspace.app.xmlui.wing.element;
042:
043: /**
044: * A class that represents a List element.
045: *
046: * The list element is used to display sets of sequential data. It contains an
047: * optional head element, as well as any number of item elements. Items contain
048: * textual information or other list elements. An item can also be associated
049: * with a label element that annotates an item with a number, a textual
050: * description of some sort, or a simple bullet. The list type (ordered,
051: * bulletted, gloss, etc.) is then determined either by the content of labels on
052: * items or by an explicit value of the "type" attribute. Note that if labels
053: * are used in conjunction with any items in a list, all of the items in that
054: * list must have a label. It is also recommended to avoid mixing label styles
055: * unless an explicit type is specified.
056: *
057: * typically rendering types are not predefined, but for lists there is a set of
058: * standard rendering options available for the rend attribute to be set too.
059: * This is not an exhaustive list.
060: *
061: * horizontal: The list should be rendered horizontally.
062: *
063: * vertical: The list should be rendered vertically.
064: *
065: * columns: The list should be rendered in equal length columns as determined by
066: * the theme.
067: *
068: * columns2: The list should be rendered in two equal columns.
069: *
070: * columns3: The list should be rendered in three equal columns.
071: *
072: * alphabet: The list should be rendered as an alphabetical index.
073: *
074: * numeric: The list should be rendered as a numeric index.
075: *
076: * @author Scott Phillips
077: */
078:
079: import java.util.ArrayList;
080:
081: import org.dspace.app.xmlui.wing.AttributeMap;
082: import org.dspace.app.xmlui.wing.Message;
083: import org.dspace.app.xmlui.wing.WingConstants;
084: import org.dspace.app.xmlui.wing.WingContext;
085: import org.dspace.app.xmlui.wing.WingException;
086: import org.xml.sax.Attributes;
087: import org.xml.sax.ContentHandler;
088: import org.xml.sax.SAXException;
089: import org.xml.sax.ext.LexicalHandler;
090: import org.xml.sax.helpers.NamespaceSupport;
091:
092: public class List extends AbstractWingElement implements
093: WingMergeableElement, StructuralElement {
094: /** The name of the list element */
095: public static final String E_LIST = "list";
096:
097: /** The name of the type attribute */
098: public static final String A_TYPE = "type";
099:
100: /** Has this element been merged? */
101: private boolean merged = false;
102:
103: /** Has a child element been merged: head, list, or item */
104: private boolean childMerged = false;
105:
106: /** The possible list types * */
107: public static final String TYPE_SIMPLE = "simple";
108:
109: public static final String TYPE_ORDERED = "ordered";
110:
111: public static final String TYPE_BULLETED = "bulleted";
112:
113: public static final String TYPE_GLOSS = "gloss";
114:
115: public static final String TYPE_PROGRESS = "progress";
116:
117: public static final String TYPE_FORM = "form";
118:
119: /** All the possible list types collected into one array */
120: public static final String[] TYPES = { TYPE_SIMPLE, TYPE_ORDERED,
121: TYPE_BULLETED, TYPE_GLOSS, TYPE_PROGRESS, TYPE_FORM };
122:
123: /** The list's name */
124: private String name;
125:
126: /** The list's type, see types above. * */
127: private String type;
128:
129: /** Any special rendering instructions * */
130: private String rend;
131:
132: /** The lists head * */
133: private Head head;
134:
135: /** All content of this container, items & lists */
136: private java.util.List<AbstractWingElement> contents = new ArrayList<AbstractWingElement>();
137:
138: /**
139: * Construct a new list.
140: *
141: *
142: * @param context
143: * (Required) The context this element is contained in, such as
144: * where to route SAX events and what i18n catalogue to use.
145: *
146: * @param name
147: * (Required) a local identifier used to differentiate the
148: * element from its siblings.
149: * @param type
150: * (May be null) determines the list type. If this is blank the
151: * list type is inferred from the context and use.
152: * @param rend
153: * (May be null) a rendering hint used to override the default
154: * display of the element. There are a set of predefined
155: * rendering values, see the class documentation above.
156: */
157: protected List(WingContext context, String name, String type,
158: String rend) throws WingException {
159: super (context);
160: require(name, "The 'name' parameter is required for all lists.");
161: restrict(
162: type,
163: TYPES,
164: "The 'type' parameter must be one of these values: 'simple', 'ordered', 'bulleted', 'gloss', or 'form'.");
165:
166: this .name = name;
167: this .type = type;
168: this .rend = rend;
169: }
170:
171: /**
172: * Set the head element which is the label associated with this list. This
173: * method should be called before any other elements have been added to the
174: * list.
175: *
176: * @param characters
177: * (May be null) Untranslated character data to be included as
178: * the list's head.
179: */
180: public Head setHead() throws WingException {
181: Head head = new Head(context, null);
182: this .head = head;
183: return head;
184: }
185:
186: /**
187: * Set the head element which is the label associated with this list. This
188: * method should be called before any other elements have been added to the
189: * list.
190: *
191: * @param characters
192: * (Required) Untranslated character data to be included as the
193: * list's head.
194: */
195: public void setHead(String characters) throws WingException {
196: Head head = setHead();
197: head.addContent(characters);
198: }
199:
200: /**
201: * Set the head element which is the label associated with this list. This
202: * method should be called before any other elements have been added to the
203: * list.
204: *
205: * @param key
206: * (Required) Key to the i18n catalogue to translate the content
207: * into the language preferred by the user.
208: */
209: public void setHead(Message key) throws WingException {
210: Head head = setHead();
211: head.addContent(key);
212: }
213:
214: /**
215: * Add a label element, they are associated with an item and annotates that
216: * item with a number, a textual description of some sort, or a simple
217: * bullet.
218: *
219: * @param name
220: * (May be null) a local identifier used to differentiate the
221: * element from its siblings.
222: * @param rend
223: * (May be null) a rendering hint used to override the default
224: * display of the element.
225: */
226: public Label addLabel(String name, String rend)
227: throws WingException {
228: Label label = new Label(context, name, rend);
229: contents.add(label);
230: return label;
231: }
232:
233: /**
234: * Add a label element, they are associated with an item and annotates that
235: * item with a number, a textual description of some sort, or a simple
236: * bullet.
237: *
238: * @param characters
239: * (Required) Untranslated character data to be included.
240: */
241: public void addLabel(String characters) throws WingException {
242: require(characters,
243: "The 'characters' parameter is required for list labels.");
244:
245: Label label = new Label(context, null, null);
246: label.addContent(characters);
247: contents.add(label);
248: }
249:
250: /**
251: * Add a label element, they are associated with an item and annotates that
252: * item with a number, a textual description of some sort, or a simple
253: * bullet. This version of label provides no textual label but may be used
254: * to indicate some implicit labeling such as ordered lists.
255: *
256: */
257: public void addLabel() throws WingException {
258: Label label = new Label(context, null, null);
259: contents.add(label);
260: }
261:
262: /**
263: * Add a label element, they are associated with an item and annotates that
264: * item with a number, a textual description of some sort, or a simple
265: * bullet.
266: *
267: * @param key
268: * (Required) Key to the i18n catalogue to translate the content
269: * into the language preferred by the user.
270: */
271: public void addLabel(Message key) throws WingException {
272: require(key, "The 'key' parameter is required for list labels.");
273:
274: Label label = new Label(context, null, null);
275: label.addContent(key);
276: contents.add(label);
277: }
278:
279: /**
280: * Add an empty unnamed item.
281: *
282: * @return a new Item
283: */
284: public Item addItem() throws WingException {
285: return addItem(null, null);
286: }
287:
288: /**
289: * Add an item element, which serves a dual purpose. It can contain other
290: * lists, allowing for hierarchies and recursive lists. Alternatively it can
291: * serve as a character container to display textual data, possibly enhanced
292: * with hyperlinks, emphasized blocks of text, images and form fields. An
293: * item cannot be both a character container and contain a list.
294: *
295: * @param name
296: * (May be null) a local identifier used to differentiate the
297: * element from its siblings.
298: * @param rend
299: * (May be null) a rendering hint used to override the default
300: * display of the element. *
301: * @return a new Item
302: */
303: public Item addItem(String name, String rend) throws WingException {
304: Item item = new Item(context, name, rend);
305: contents.add(item);
306: return item;
307: }
308:
309: /**
310: * Add an item element that contains only character content.
311: *
312: * @param characters
313: * (Required) Untranslated character data to be included.
314: */
315: public void addItem(String characters) throws WingException {
316: require(characters,
317: "The 'characters' parameter is required for list items.");
318:
319: Item item = this .addItem(null, null);
320: item.addContent(characters);
321: }
322:
323: /**
324: * Add an item element that contains only translated content.
325: *
326: * @param key
327: * (Required) Key to the i18n catalogue to translate the content
328: * into the language preferred by the user.
329: */
330: public void addItem(Message key) throws WingException {
331: require(key, "The 'key' parameter is required for list items.");
332:
333: Item item = this .addItem(null, null);
334: item.addContent(key);
335: }
336:
337: /**
338: * Add an item to the list that contains a link. The link will consist of
339: * the given content and linked to the given target.
340: *
341: * @param target
342: * (Required) The link target.
343: * @param characters
344: * (Required) Untranslated character data to be included as the
345: * link's body.
346: */
347: public void addItemXref(String target, String characters)
348: throws WingException {
349: Item item = this .addItem(null, null);
350: item.addXref(target, characters);
351: }
352:
353: /**
354: * Add an item to the list that contains a link. The link will consist of
355: * the given content and linked to the given target.
356: *
357: * @param target
358: * (Required) The link target.
359: * @param key
360: * (Required) i18n key for translating content into the user's
361: * preferred language.
362: */
363: public void addItemXref(String target, Message key)
364: throws WingException {
365:
366: Item item = this .addItem(null, null);
367: item.addXref(target, key);
368: }
369:
370: /**
371: * Add a new sublist to this list.
372: *
373: * @param name
374: * (Required) a local identifier used to differentiate the
375: * element from its siblings.
376: * @param type
377: * (May be null) determines the list type. If this is blank the
378: * list type is inferred from the context and use.
379: * @param rend
380: * (May be null) a rendering hint used to override the default
381: * display of the element.
382: * @return A new sub list.
383: */
384: public List addList(String name, String type, String rend)
385: throws WingException {
386: List list = new List(context, name, type, rend);
387: contents.add(list);
388: return list;
389: }
390:
391: /**
392: * Add a new sublist to this list.
393: *
394: * @param name
395: * (Required) a local identifier used to differentiate the
396: * element from its siblings.
397: * @param type
398: * (May be null) determines the list type. If this is blank the
399: * list type is inferred from the context and use.
400: * @return A new sub list.
401: */
402: public List addList(String name, String type) throws WingException {
403: List list = new List(context, name, type, null);
404: contents.add(list);
405: return list;
406: }
407:
408: /**
409: * Add a new sublist to this list.
410: *
411: * @param name
412: * (Required) a local identifier used to differentiate the
413: * element from its siblings.
414: * @return A new sub list.
415: */
416: public List addList(String name) throws WingException {
417: return addList(name, null, null);
418: }
419:
420: /**
421: * Determine if the given SAX startElement event is equivalent to this list.
422: *
423: * @param namespace
424: * The element's name space
425: * @param localName
426: * The local, unqualified, name for this element
427: * @param qName
428: * The qualified name for this element
429: * @param attributes
430: * The element's attributes
431: * @return True if this list is equivalent to the given SAX Event.
432: */
433: public boolean mergeEqual(String namespace, String localName,
434: String qName, Attributes attributes) {
435: // Check if it's in our name space and an options element.
436: if (!WingConstants.DRI.URI.equals(namespace))
437: return false;
438: if (!E_LIST.equals(localName))
439: return false;
440: String name = attributes.getValue(A_NAME);
441: if (name == null)
442: return false;
443: if (!name.equals(this .name))
444: return false;
445: return true;
446: }
447:
448: /**
449: * Merge the given SAX startElement event into this list's child. If this
450: * SAX event matches a child element of this list then it should be removed
451: * from the internal book keep of this element and returned. Typically this
452: * is accomplished by looping through all children elements and returned the
453: * first one that returns true for the mergeEqual method.
454: *
455: * @param namespace
456: * The element's name space
457: * @param localName
458: * The local, unqualified, name for this element *
459: * @param qName
460: * The qualified name for this element
461: * @param attributes
462: * The element's attributes
463: * @return The child element
464: */
465: public WingMergeableElement mergeChild(String namespace,
466: String localName, String qName, Attributes attributes)
467: throws SAXException, WingException {
468: this .childMerged = true;
469:
470: WingMergeableElement found = null;
471: for (AbstractWingElement content : contents) {
472: if (content instanceof WingMergeableElement) {
473: WingMergeableElement candidate = (WingMergeableElement) content;
474: if (candidate.mergeEqual(namespace, localName, qName,
475: attributes))
476: found = candidate;
477: }
478: }
479: contents.remove(found);
480: return found;
481: }
482:
483: /**
484: * Inform this list that it is being merged with an existing element.
485: * Practically this means that when this method is being transformed to SAX
486: * it should assume that the element's SAX events have all ready been sent.
487: * In this case the element would only need to transform to SAX the children
488: * of this element.
489: *
490: * Further more if the element needs to add any attributes to the SAX
491: * startElement event it may modify the attributes object passed to make
492: * changes.
493: *
494: * @return The attributes for this merged element
495: */
496: public Attributes merge(Attributes attributes) throws SAXException,
497: WingException {
498: this .merged = true;
499: return attributes;
500: }
501:
502: /**
503: * Translate this element and all contained elements into SAX events. The
504: * events should be routed to the contentHandler found in the WingContext.
505: *
506: * @param contentHandler
507: * (Required) The registered contentHandler where SAX events
508: * should be routed too.
509: * @param lexicalHandler
510: * (Required) The registered lexicalHandler where lexical
511: * events (such as CDATA, DTD, etc) should be routed too.
512: * @param namespaces
513: * (Required) SAX Helper class to keep track of namespaces able
514: * to determine the correct prefix for a given namespace URI.
515: */
516: public void toSAX(ContentHandler contentHandler,
517: LexicalHandler lexicalHandler, NamespaceSupport namespaces)
518: throws SAXException {
519:
520: if (this .merged == false) {
521: AttributeMap attributes = new AttributeMap();
522: attributes.put(A_NAME, this .name);
523: attributes.put(A_ID, this .context.generateID(E_LIST,
524: this .name));
525: if (this .type != null)
526: attributes.put(A_TYPE, this .type);
527: if (this .rend != null)
528: attributes.put(A_RENDER, this .rend);
529:
530: startElement(contentHandler, namespaces, E_LIST, attributes);
531:
532: }
533:
534: if (childMerged == false && head != null)
535: head.toSAX(contentHandler, lexicalHandler, namespaces);
536:
537: for (AbstractWingElement content : contents)
538: content.toSAX(contentHandler, lexicalHandler, namespaces);
539:
540: if (this .merged == false)
541: endElement(contentHandler, namespaces, E_LIST);
542: }
543:
544: /**
545: * dispose
546: */
547: public void dispose() {
548: if (head != null)
549: head.dispose();
550: head = null;
551: for (AbstractWingElement content : contents)
552: content.dispose();
553: contents.clear();
554: contents = null;
555: super.dispose();
556: }
557: }
|