001: /* *************************************************************************
002:
003: Millstone(TM)
004: Open Sourced User Interface Library for
005: Internet Development with Java
006:
007: Millstone is a registered trademark of IT Mill Ltd
008: Copyright (C) 2000-2005 IT Mill Ltd
009:
010: *************************************************************************
011:
012: This library is free software; you can redistribute it and/or
013: modify it under the terms of the GNU Lesser General Public
014: license version 2.1 as published by the Free Software Foundation.
015:
016: This library is distributed in the hope that it will be useful,
017: but WITHOUT ANY WARRANTY; without even the implied warranty of
018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: Lesser General Public License for more details.
020:
021: You should have received a copy of the GNU Lesser General Public
022: License along with this library; if not, write to the Free Software
023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: *************************************************************************
026:
027: For more information, contact:
028:
029: IT Mill Ltd phone: +358 2 4802 7180
030: Ruukinkatu 2-4 fax: +358 2 4802 7181
031: 20540, Turku email: info@itmill.com
032: Finland company www: www.itmill.com
033:
034: Primary source for MillStone information and releases: www.millstone.org
035:
036: ********************************************************************** */
037:
038: package org.millstone.base.ui;
039:
040: import java.lang.reflect.Method;
041:
042: import org.millstone.base.terminal.PaintTarget;
043: import org.millstone.base.terminal.PaintException;
044: import org.millstone.base.data.Property;
045: import org.millstone.base.data.util.ObjectProperty;
046:
047: /** Label component for showing non-editable short texts.
048: *
049: * The label content can be set to the modes specified by the final members
050: * CONTENT_*
051: *
052: * <p>The contents of the label may contain simple
053: * formatting:
054: * <ul>
055: * <li> <b><b></b> Bold
056: * <li> <b><i></b> Italic
057: * <li> <b><u></b> Underlined
058: * <li> <b><br/></b> Linebreak
059: * <li> <b><ul><li>item 1</li><li>item 2</li></ul></b> List of items
060: * </ul>
061: * The <b>b</b>,<b>i</b>,<b>u</b> and <b>li</b> tags can contain all the
062: * tags in the list recursively.
063: * </p>
064: *
065: * @author IT Mill Ltd.
066: * @version 3.1.1
067: * @since 3.0
068: */
069: public class Label extends AbstractComponent implements Property,
070: Property.Viewer, Property.ValueChangeListener,
071: Property.ValueChangeNotifier, Comparable {
072:
073: /** Content mode, where the label contains only plain text. The getValue()
074: * result is coded to XML when painting.
075: */
076: public static final int CONTENT_TEXT = 0;
077:
078: /** Content mode, where the label contains preformatted text.
079: */
080: public static final int CONTENT_PREFORMATTED = 1;
081:
082: /** Formatted content mode, where the contents is XML restricted to the
083: * UIDL 1.0 formatting markups.
084: */
085: public static final int CONTENT_UIDL = 2;
086:
087: /** Content mode, where the label contains XHTML. Contents is then enclosed in
088: * DIV elements having namespace of "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd".
089: */
090: public static final int CONTENT_XHTML = 3;
091:
092: /** Content mode, where the label contains well-formed or well-balanced XML.
093: * Each of the root elements must have their default namespace specified.
094: */
095: public static final int CONTENT_XML = 4;
096:
097: /** Content mode, where the label contains RAW output. Output is not
098: * required to comply to with XML. In Web Adapter output is inserted inside
099: * the resulting HTML document as-is. This is useful for some specific
100: * purposes where possibly broken HTML content needs to be shown, but in
101: * most cases XHTML mode should be preferred.
102: */
103: public static final int CONTENT_RAW = 5;
104:
105: /** The default content mode is plain text */
106: public static final int CONTENT_DEFAULT = CONTENT_TEXT;
107:
108: private Property dataSource;
109: private int contentMode = CONTENT_DEFAULT;
110:
111: /** Creates an empty Label. */
112: public Label() {
113: setPropertyDataSource(new ObjectProperty("", String.class));
114: }
115:
116: /** Creates a new instance of Label with text-contents. */
117: public Label(String content) {
118: setPropertyDataSource(new ObjectProperty(content, String.class));
119: }
120:
121: /** Creates a new instance of Label with text-contents read from given datasource. */
122: public Label(Property contentSource) {
123: setPropertyDataSource(contentSource);
124: }
125:
126: /** Creates a new instance of Label with text-contents. */
127: public Label(String content, int contentMode) {
128: setPropertyDataSource(new ObjectProperty(content, String.class));
129: setContentMode(contentMode);
130: }
131:
132: /** Creates a new instance of Label with text-contents read from given datasource. */
133: public Label(Property contentSource, int contentMode) {
134: setPropertyDataSource(contentSource);
135: setContentMode(contentMode);
136: }
137:
138: /** Get component UIDL tag.
139: * @return Component UIDL tag as string.
140: */
141: public String getTag() {
142: return "label";
143: }
144:
145: /** Set the component to read-only.
146: * Readonly is not used in label.
147: * @param readOnly True to enable read-only mode, False to disable it
148: */
149: public void setReadOnly(boolean readOnly) {
150: if (dataSource == null)
151: throw new IllegalStateException("Datasource must be se");
152: dataSource.setReadOnly(readOnly);
153: }
154:
155: /** Is the component read-only ?
156: * Readonly is not used in label - this returns allways false.
157: * @return True iff the component is in read only mode
158: */
159: public boolean isReadOnly() {
160: if (dataSource == null)
161: throw new IllegalStateException("Datasource must be se");
162: return dataSource.isReadOnly();
163: }
164:
165: /** Paint the content of this component.
166: * @param event PaintEvent.
167: */
168: public void paintContent(PaintTarget target) throws PaintException {
169: if (contentMode == CONTENT_TEXT)
170: target.addText(toString());
171: else if (contentMode == CONTENT_UIDL)
172: target.addUIDL(toString());
173: else if (contentMode == CONTENT_XHTML) {
174: target.startTag("data");
175: target
176: .addXMLSection("div", toString(),
177: "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd");
178: target.endTag("data");
179: } else if (contentMode == CONTENT_PREFORMATTED) {
180: target.startTag("pre");
181: target.addText(toString());
182: target.endTag("pre");
183: } else if (contentMode == CONTENT_XML) {
184: target.addXMLSection("data", toString(), null);
185: } else if (contentMode == CONTENT_RAW) {
186: target.startTag("data");
187: target.addAttribute("escape", false);
188: target.addCharacterData(toString());
189: target.endTag("data");
190: }
191:
192: }
193:
194: /** Get the value of the label.
195: * Value of the label is the XML contents of the label.
196: * @return Value of the label
197: */
198: public Object getValue() {
199: if (dataSource == null)
200: throw new IllegalStateException("Datasource must be se");
201: return dataSource.getValue();
202: }
203:
204: /** Set the value of the label.
205: * Value of the label is the XML contents of the label.
206: * @param newValue New value of the label
207: */
208: public void setValue(Object newValue) {
209: if (dataSource == null)
210: throw new IllegalStateException("Datasource must be se");
211: this .dataSource.setValue(newValue);
212: }
213:
214: public String toString() {
215: if (dataSource == null)
216: throw new IllegalStateException("Datasource must be se");
217: return dataSource.toString();
218: }
219:
220: public Class getType() {
221: if (dataSource == null)
222: throw new IllegalStateException("Datasource must be se");
223: return dataSource.getType();
224: }
225:
226: /** Get viewing data-source property. */
227: public Property getPropertyDataSource() {
228: return dataSource;
229: }
230:
231: /** Set the property as data-source for viewing. */
232: public void setPropertyDataSource(Property newDataSource) {
233: // Stop listening the old data source changes
234: if (dataSource != null
235: && Property.ValueChangeNotifier.class
236: .isAssignableFrom(dataSource.getClass()))
237: ((Property.ValueChangeNotifier) dataSource)
238: .removeListener(this );
239:
240: // Set the new data source
241: dataSource = newDataSource;
242:
243: // Listen the new data source if possible
244: if (dataSource != null
245: && Property.ValueChangeNotifier.class
246: .isAssignableFrom(dataSource.getClass()))
247: ((Property.ValueChangeNotifier) dataSource)
248: .addListener(this );
249: }
250:
251: /** Get the content mode of the Label.
252: *
253: * <p>Possible content modes include:
254: * <ul>
255: * <li><b>CONTENT_TEXT</b>
256: * Content mode, where the label contains only plain text. The
257: * getValue() result is coded to XML when painting.</li>
258: * <li><b>CONTENT_PREFORMATTED</b>
259: * Content mode, where the label contains preformatted text.</li>
260: * <li><b>CONTENT_UIDL</b>
261: * Formatted content mode, where the contents is XML restricted to
262: * the UIDL 1.0 formatting markups.</li>
263: * <li><b>CONTENT_XHTML</b>
264: * Content mode, where the label contains XHTML. Contents is then
265: * enclosed in DIV elements having namespace of
266: * "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd".</li>
267: * <li><b>CONTENT_XML</b>
268: * Content mode, where the label contains well-formed or
269: * well-balanced XML. Each of the root elements must have their
270: * default namespace specified.</li>
271: * <li><b>CONTENT_RAW</b>
272: * Content mode, where the label contains RAW output. Output is not
273: * required to comply to with XML. In Web Adapter output is
274: * inserted inside the resulting HTML document as-is. This is
275: * useful for some specific purposes where possibly broken HTML
276: * content needs to be shown, but in most cases XHTML mode should
277: * be preferred.</li>
278: * </ul></p>
279: *
280: * @return Content mode of the label.
281: */
282: public int getContentMode() {
283: return contentMode;
284: }
285:
286: /** Set the content mode of the Label.
287: *
288: * <p>Possible content modes include:
289: * <ul>
290: * <li><b>CONTENT_TEXT</b>
291: * Content mode, where the label contains only plain text. The
292: * getValue() result is coded to XML when painting.</li>
293: * <li><b>CONTENT_PREFORMATTED</b>
294: * Content mode, where the label contains preformatted text.</li>
295: * <li><b>CONTENT_UIDL</b>
296: * Formatted content mode, where the contents is XML restricted to
297: * the UIDL 1.0 formatting markups.</li>
298: * <li><b>CONTENT_XHTML</b>
299: * Content mode, where the label contains XHTML. Contents is then
300: * enclosed in DIV elements having namespace of
301: * "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd".</li>
302: * <li><b>CONTENT_XML</b>
303: * Content mode, where the label contains well-formed or
304: * well-balanced XML. Each of the root elements must have their
305: * default namespace specified.</li>
306: * <li><b>CONTENT_RAW</b>
307: * Content mode, where the label contains RAW output. Output is not
308: * required to comply to with XML. In Web Adapter output is
309: * inserted inside the resulting HTML document as-is. This is
310: * useful for some specific purposes where possibly broken HTML
311: * content needs to be shown, but in most cases XHTML mode should
312: * be preferred.</li>
313: * </ul></p>
314: *
315: * @param contentMode New content mode of the label.
316: */
317: public void setContentMode(int contentMode) {
318: if (contentMode >= CONTENT_TEXT && contentMode <= CONTENT_RAW)
319: this .contentMode = contentMode;
320: }
321:
322: /* Value change events ****************************************** */
323:
324: private static final Method VALUE_CHANGE_METHOD;
325:
326: static {
327: try {
328: VALUE_CHANGE_METHOD = Property.ValueChangeListener.class
329: .getDeclaredMethod(
330: "valueChange",
331: new Class[] { Property.ValueChangeEvent.class });
332: } catch (java.lang.NoSuchMethodException e) {
333: // This should never happen
334: throw new java.lang.RuntimeException();
335: }
336: }
337:
338: /** Value change event
339: * @author IT Mill Ltd.
340: * @version 3.1.1
341: * @since 3.0
342: */
343: public class ValueChangeEvent extends Component.Event implements
344: Property.ValueChangeEvent {
345:
346: /**
347: * Serial generated by eclipse.
348: */
349: private static final long serialVersionUID = 3906084563938586935L;
350:
351: /** New instance of text change event
352: * @param source Source of the event.
353: */
354: public ValueChangeEvent(Label source) {
355: super (source);
356: }
357:
358: /** Value where the event occurred
359: * @return Source of the event.
360: */
361: public Property getProperty() {
362: return (Property) getSource();
363: }
364: }
365:
366: /** Add value change listener
367: * @param listener Listener to be added.
368: */
369: public void addListener(Property.ValueChangeListener listener) {
370: addListener(Label.ValueChangeEvent.class, listener,
371: VALUE_CHANGE_METHOD);
372: }
373:
374: /** Remove value change listener
375: * @param listener Listener to be removed.
376: */
377: public void removeListener(Property.ValueChangeListener listener) {
378: removeListener(Label.ValueChangeEvent.class, listener,
379: VALUE_CHANGE_METHOD);
380: }
381:
382: /** Emit options change event. */
383: protected void fireValueChange() {
384: // Set the error message
385: fireEvent(new Label.ValueChangeEvent(this ));
386: requestRepaint();
387: }
388:
389: /** Listen value change events from data source.
390: * @see org.millstone.base.data.Property.ValueChangeListener#valueChange(Property.ValueChangeEvent)
391: */
392: public void valueChange(Property.ValueChangeEvent event) {
393: fireValueChange();
394: }
395:
396: /** Compare Label to other objects.
397: *
398: * <p>Labels can be compared to other labels for sorting label contents.
399: * This is especially handy for sorting table columns.</p>
400: *
401: * <p>In RAW, PREFORMATTED and TEXT modes, the label contents are
402: * compared as is. In XML, UIDL and XHTML modes, only CDATA is compared and
403: * tags ignored. If the other object is not a Label, its toString() return
404: * value is used in comparison.</p>
405: *
406: * @see java.lang.Comparable#compareTo(java.lang.Object)
407: * @param other Other object to compare to
408: * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
409: */
410: public int compareTo(Object other) {
411:
412: String this Value;
413: String otherValue;
414:
415: if (contentMode == CONTENT_XML || contentMode == CONTENT_UIDL
416: || contentMode == CONTENT_XHTML)
417: this Value = stripTags(toString());
418: else
419: this Value = toString();
420:
421: if (other instanceof Label
422: && (((Label) other).getContentMode() == CONTENT_XML
423: || ((Label) other).getContentMode() == CONTENT_UIDL || ((Label) other)
424: .getContentMode() == CONTENT_XHTML))
425: otherValue = stripTags(other.toString());
426: else
427: otherValue = other.toString();
428:
429: return this Value.compareTo(otherValue);
430: }
431:
432: /** Strip tags from the XML.
433: *
434: * @param xml String containing a XML snippet
435: * @return The original XML without tags
436: */
437: private String stripTags(String xml) {
438:
439: StringBuffer res = new StringBuffer();
440:
441: int processed = 0;
442: int xmlLen = xml.length();
443: while (processed < xmlLen) {
444: int next = xml.indexOf('<', processed);
445: if (next < 0)
446: next = xmlLen;
447: res.append(xml.substring(processed, next));
448: if (processed < xmlLen) {
449: next = xml.indexOf('>', processed);
450: if (next < 0)
451: next = xmlLen;
452: processed = next + 1;
453: }
454: }
455:
456: return res.toString();
457: }
458:
459: }
|