001: /**
002: * ===========================================
003: * JFreeReport : a free Java reporting library
004: * ===========================================
005: *
006: * Project Info: http://reporting.pentaho.org/
007: *
008: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
009: *
010: * This library is free software; you can redistribute it and/or modify it under the terms
011: * of the GNU Lesser General Public License as published by the Free Software Foundation;
012: * either version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015: * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016: * See the GNU Lesser General Public License for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public License along with this
019: * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: *
022: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023: * in the United States and other countries.]
024: *
025: * ------------
026: * EncodingComboBoxModel.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.modules.gui.commonswing;
030:
031: import java.io.BufferedInputStream;
032: import java.io.IOException;
033: import java.io.InputStream;
034: import java.io.Serializable;
035: import java.util.ArrayList;
036: import java.util.Collections;
037: import java.util.Comparator;
038: import java.util.Enumeration;
039: import java.util.Locale;
040: import java.util.Properties;
041: import java.util.ResourceBundle;
042: import javax.swing.ComboBoxModel;
043: import javax.swing.event.ListDataEvent;
044: import javax.swing.event.ListDataListener;
045:
046: import org.jfree.fonts.encoding.EncodingRegistry;
047: import org.jfree.report.JFreeReportBoot;
048: import org.jfree.report.util.i18n.Messages;
049: import org.jfree.util.Log;
050: import org.jfree.util.ObjectUtilities;
051:
052: /**
053: * A model for the 'encoding' combo box. This combobox model presents a selection for all
054: * available string encodings.
055: *
056: * @author Thomas Morgner.
057: */
058: public class EncodingComboBoxModel implements ComboBoxModel {
059: /**
060: * The property that defines which encodings are available in the export dialogs.
061: */
062: public static final String AVAILABLE_ENCODINGS = "org.jfree.report.modules.gui.base.EncodingsAvailable"; //$NON-NLS-1$
063:
064: /**
065: * The encodings available properties value for all properties.
066: */
067: public static final String AVAILABLE_ENCODINGS_ALL = "all"; //$NON-NLS-1$
068: /**
069: * The encodings available properties value for properties defined in the properties
070: * file.
071: */
072: public static final String AVAILABLE_ENCODINGS_FILE = "file"; //$NON-NLS-1$
073: /**
074: * The encodings available properties value for no properties defined. The encoding
075: * selection will be disabled.
076: */
077: public static final String AVAILABLE_ENCODINGS_NONE = "none"; //$NON-NLS-1$
078:
079: /**
080: * The name of the properties file used to define the available encodings. The property
081: * points to a resources in the classpath, not to a real file!
082: */
083: public static final String ENCODINGS_DEFINITION_FILE = "org.jfree.report.modules.gui.base.EncodingsFile"; //$NON-NLS-1$
084:
085: /**
086: * The default name for the encoding properties file. This property defaults to
087: * "/org/jfree/report/jfreereport-encodings.properties".
088: */
089: public static final String ENCODINGS_DEFINITION_FILE_DEFAULT = "org/jfree/report/modules/gui/commonswing/jfreereport-encodings.properties"; //$NON-NLS-1$
090:
091: /**
092: * An encoding comparator.
093: */
094: private static class EncodingCarrierComparator implements
095: Comparator, Serializable {
096: protected EncodingCarrierComparator() {
097: }
098:
099: /**
100: * Compares its two arguments for order. Returns a negative integer, zero, or a
101: * positive integer as the first argument is less than, equal to, or greater than the
102: * second.
103: *
104: * @param o1 the first object to be compared.
105: * @param o2 the second object to be compared.
106: * @return a negative integer, zero, or a positive integer as the first argument is
107: * less than, equal to, or greater than the second.
108: *
109: * @throws java.lang.ClassCastException if the arguments' types prevent them from
110: * being compared by this Comparator.
111: */
112: public int compare(final Object o1, final Object o2) {
113: final EncodingCarrier e1 = (EncodingCarrier) o1;
114: final EncodingCarrier e2 = (EncodingCarrier) o2;
115: return e1.getName().toLowerCase().compareTo(
116: e2.getName().toLowerCase());
117: }
118:
119: /**
120: * Returns <code>true</code> if this object is equal to <code>o</code>, and
121: * <code>false</code> otherwise.
122: *
123: * @param o the object.
124: * @return A boolean.
125: */
126: public boolean equals(final Object o) {
127: if (o == null) {
128: return false;
129: }
130: return getClass().equals(o.getClass());
131: }
132:
133: /**
134: * All comparators of this type are equal.
135: *
136: * @return A hash code.
137: */
138: public int hashCode() {
139: return getClass().hashCode();
140: }
141: }
142:
143: /**
144: * An encoding carrier.
145: */
146: private static class EncodingCarrier {
147: /**
148: * The encoding name.
149: */
150: private String name;
151:
152: /**
153: * The encoding description.
154: */
155: private String description;
156:
157: /**
158: * The display name.
159: */
160: private String displayName;
161:
162: /**
163: * Creates a new encoding.
164: *
165: * @param name the name (<code>null</code> not permitted).
166: * @param description the description.
167: */
168: protected EncodingCarrier(final String name,
169: final String description) {
170: if (name == null) {
171: throw new NullPointerException();
172: }
173: this .name = name;
174: this .description = description;
175: final StringBuffer dName = new StringBuffer();
176: dName.append(name);
177: dName.append(" ("); //$NON-NLS-1$
178: dName.append(description);
179: dName.append(')');
180: this .displayName = dName.toString();
181: }
182:
183: /**
184: * Returns the name.
185: *
186: * @return The name.
187: */
188: public String getName() {
189: return name;
190: }
191:
192: /**
193: * Returns the description.
194: *
195: * @return The description.
196: */
197: public String getDescription() {
198: return description;
199: }
200:
201: /**
202: * Returns the display name (the regular name followed by the description in
203: * brackets).
204: *
205: * @return The display name.
206: */
207: public String getDisplayName() {
208: return displayName;
209: }
210:
211: /**
212: * Returns <code>true</code> if the objects are equal, and <code>false</code>
213: * otherwise.
214: *
215: * @param o the object.
216: * @return A boolean.
217: */
218: public boolean equals(final Object o) {
219: if (this == o) {
220: return true;
221: }
222: if (!(o instanceof EncodingCarrier)) {
223: return false;
224: }
225:
226: final EncodingCarrier carrier = (EncodingCarrier) o;
227:
228: if (!name.equalsIgnoreCase(carrier.name)) {
229: return false;
230: }
231:
232: return true;
233: }
234:
235: /**
236: * Returns a hash code.
237: *
238: * @return The hash code.
239: */
240: public int hashCode() {
241: return name.hashCode();
242: }
243: }
244:
245: /**
246: * Storage for the encodings.
247: */
248: private final ArrayList encodings;
249:
250: /**
251: * Storage for registered listeners.
252: */
253: private ArrayList listDataListeners;
254:
255: /**
256: * The selected index.
257: */
258: private int selectedIndex;
259:
260: /**
261: * The selected object.
262: */
263: private Object selectedObject;
264:
265: private ResourceBundle bundle;
266: public static final String BUNDLE_NAME = "org.jfree.report.modules.gui.commonswing.encoding-names"; //$NON-NLS-1$
267:
268: private Messages messages;
269: private String ENCODING_DEFAULT_DESCRIPTION;
270:
271: /**
272: * Creates a new model.
273: * @param locale
274: */
275: public EncodingComboBoxModel(final Locale locale) {
276: bundle = ResourceBundle.getBundle(BUNDLE_NAME, locale);
277: messages = new Messages(locale, SwingCommonModule.BUNDLE_NAME);
278: ENCODING_DEFAULT_DESCRIPTION = messages
279: .getString("EncodingComboBoxModel.USER_ENCODING_DEFAULT_DESCRIPTION"); //$NON-NLS-1$
280: encodings = new ArrayList();
281: listDataListeners = null;
282: selectedIndex = -1;
283: }
284:
285: /**
286: * Adds an encoding.
287: *
288: * @param name the name.
289: * @param description the description.
290: * @return <code>true</code> if the encoding is valid and added to the model,
291: * <code>false</code> otherwise.
292: */
293: public boolean addEncoding(final String name,
294: final String description) {
295: if (EncodingRegistry.getInstance().isSupportedEncoding(name)) {
296: encodings.add(new EncodingCarrier(name, description));
297: } else {
298: return false;
299: }
300:
301: fireContentsChanged();
302: return true;
303: }
304:
305: /**
306: * Adds an encoding to the model without checking its validity.
307: *
308: * @param name the name.
309: * @param description the description.
310: */
311: public void addEncodingUnchecked(final String name,
312: final String description) {
313: encodings.add(new EncodingCarrier(name, description));
314: fireContentsChanged();
315: }
316:
317: public void removeEncoding(final String name) {
318: if (encodings.remove(name)) {
319: fireContentsChanged();
320: }
321: }
322:
323: /**
324: * Make sure, that this encoding is defined and selectable in the combobox model.
325: *
326: * @param encoding the encoding that should be verified.
327: */
328: public void ensureEncodingAvailable(final String encoding) {
329: if (encoding == null) {
330: throw new NullPointerException(
331: messages
332: .getErrorString("EncodingComboBoxModel.ERROR_0001_ENCODING_CANNOT_BE_NULL")); //$NON-NLS-1$
333: }
334: final String desc = getEncodingDescription(encoding);
335: final EncodingCarrier ec = new EncodingCarrier(encoding, desc);
336: if (encodings.contains(ec) == false) {
337: encodings.add(ec);
338: fireContentsChanged();
339: }
340: }
341:
342: protected String getEncodingDescription(final String encoding) {
343: try {
344: return bundle.getString(encoding);
345: } catch (Exception e) {
346: return ENCODING_DEFAULT_DESCRIPTION;
347: }
348: }
349:
350: /**
351: * Sorts the encodings. Keep the selected object ...
352: */
353: public void sort() {
354: final Object selectedObject = getSelectedItem();
355: Collections.sort(encodings, new EncodingCarrierComparator());
356: setSelectedItem(selectedObject);
357: fireContentsChanged();
358: }
359:
360: /**
361: * Notifies all registered listeners that the content of the model has changed.
362: */
363: protected void fireContentsChanged() {
364: if (listDataListeners == null) {
365: return;
366: }
367: fireContentsChanged(0, getSize());
368: }
369:
370: /**
371: * Notifies all registered listeners that the content of the model has changed.
372: */
373: protected void fireContentsChanged(final int start, final int length) {
374: if (listDataListeners == null) {
375: return;
376: }
377: final ListDataEvent evt = new ListDataEvent(this ,
378: ListDataEvent.CONTENTS_CHANGED, start, length);
379: for (int i = 0; i < listDataListeners.size(); i++) {
380: final ListDataListener l = (ListDataListener) listDataListeners
381: .get(i);
382: l.contentsChanged(evt);
383: }
384: }
385:
386: /**
387: * Set the selected item. The implementation of this method should notify all
388: * registered <code>ListDataListener</code>s that the contents have changed.
389: *
390: * @param anItem the list object to select or <code>null</code> to clear the selection
391: */
392: public void setSelectedItem(final Object anItem) {
393: selectedObject = anItem;
394: if (anItem instanceof String) {
395: final int size = getSize();
396: for (int i = 0; i < size; i++) {
397: if (anItem.equals(getElementAt(i))) {
398: selectedIndex = i;
399: fireContentsChanged(-1, -1);
400: return;
401: }
402: }
403: }
404: selectedIndex = -1;
405: fireContentsChanged(-1, -1);
406: }
407:
408: /**
409: * Returns the selected index.
410: *
411: * @return The index.
412: */
413: public int getSelectedIndex() {
414: return selectedIndex;
415: }
416:
417: /**
418: * Defines the selected index for this encoding model.
419: *
420: * @param index the selected index or -1 to clear the selection.
421: * @throws java.lang.IllegalArgumentException
422: * if the given index is invalid.
423: */
424: public void setSelectedIndex(final int index) {
425: if (index == -1) {
426: selectedIndex = -1;
427: selectedObject = null;
428: fireContentsChanged(-1, -1);
429: return;
430: }
431: if (index < -1 || index >= getSize()) {
432: throw new IllegalArgumentException(
433: messages
434: .getErrorString("EncodingComboBoxModel.ERROR_0001_INVALID_INDEX")); //$NON-NLS-1$
435: }
436: selectedIndex = index;
437: selectedObject = getElementAt(index);
438: fireContentsChanged(-1, -1);
439: }
440:
441: /**
442: * Returns the selected encoding.
443: *
444: * @return The encoding (name).
445: */
446: public String getSelectedEncoding() {
447: if (selectedIndex == -1) {
448: return null;
449: }
450: final EncodingCarrier ec = (EncodingCarrier) encodings
451: .get(selectedIndex);
452: return ec.getName();
453: }
454:
455: /**
456: * Returns the selected item.
457: *
458: * @return The selected item or <code>null</code> if there is no selection
459: */
460: public Object getSelectedItem() {
461: return selectedObject;
462: }
463:
464: /**
465: * Returns the length of the list.
466: *
467: * @return the length of the list
468: */
469: public int getSize() {
470: return encodings.size();
471: }
472:
473: /**
474: * Returns the value at the specified index.
475: *
476: * @param index the requested index
477: * @return the value at <code>index</code>
478: */
479: public Object getElementAt(final int index) {
480: final EncodingCarrier ec = (EncodingCarrier) encodings
481: .get(index);
482: return ec.getDisplayName();
483: }
484:
485: /**
486: * Adds a listener to the list that's notified each time a change to the data model
487: * occurs.
488: *
489: * @param l the <code>ListDataListener</code> to be added
490: */
491: public void addListDataListener(final ListDataListener l) {
492: if (listDataListeners == null) {
493: listDataListeners = new ArrayList(5);
494: }
495: listDataListeners.add(l);
496: }
497:
498: /**
499: * Removes a listener from the list that's notified each time a change to the data model
500: * occurs.
501: *
502: * @param l the <code>ListDataListener</code> to be removed
503: */
504: public void removeListDataListener(final ListDataListener l) {
505: if (listDataListeners == null) {
506: return;
507: }
508: listDataListeners.remove(l);
509: }
510:
511: /**
512: * Creates a default model containing a selection of encodings.
513: *
514: * @return The default model.
515: */
516: public static EncodingComboBoxModel createDefaultModel(
517: final Locale locale) {
518: final EncodingComboBoxModel ecb = new EncodingComboBoxModel(
519: locale);
520:
521: final String availEncs = getAvailableEncodings();
522: final boolean allEncodings = availEncs
523: .equalsIgnoreCase(AVAILABLE_ENCODINGS_ALL);
524:
525: if (allEncodings || availEncs.equals(AVAILABLE_ENCODINGS_FILE)) {
526: final String encFile = getEncodingsDefinitionFile();
527: final InputStream in = ObjectUtilities.getResourceAsStream(
528: encFile, EncodingComboBoxModel.class);
529: if (in == null) {
530: final Messages messages = new Messages(locale,
531: SwingCommonModule.BUNDLE_NAME);
532: Log
533: .warn(messages
534: .getString(
535: "EncodingComboBoxModel.WARN_ENCODING_FILE_NOT_FOUND", encFile)); //$NON-NLS-1$
536: } else {
537: try {
538: // final Properties defaultEncodings = getDefaultEncodings();
539: final Properties encDef = new Properties();
540: final BufferedInputStream bin = new BufferedInputStream(
541: in);
542: try {
543: encDef.load(bin);
544: } finally {
545: bin.close();
546: }
547: final Enumeration en = encDef.keys();
548: while (en.hasMoreElements()) {
549: final String enc = (String) en.nextElement();
550: // if not set to "true"
551: if ("true".equalsIgnoreCase(encDef.getProperty(enc, "false"))) //$NON-NLS-1$ //$NON-NLS-2$
552: {
553: // if the encoding is disabled ...
554: ecb.addEncoding(enc, ecb
555: .getEncodingDescription(enc));
556: }
557: }
558: } catch (IOException e) {
559: final Messages messages = new Messages(locale,
560: SwingCommonModule.BUNDLE_NAME);
561: Log
562: .warn(new Log.SimpleMessage(
563: messages
564: .getString(
565: "EncodingComboBoxModel.WARN_ERROR_READING_ENCODING_FILE", encFile), e)); //$NON-NLS-1$
566: }
567: }
568: }
569: return ecb;
570: }
571:
572: /**
573: * Returns the index of an encoding.
574: *
575: * @param encoding the encoding (name).
576: * @return The index.
577: */
578: public int indexOf(final String encoding) {
579: return encodings.indexOf(new EncodingCarrier(encoding, null));
580: }
581:
582: /**
583: * Returns an encoding.
584: *
585: * @param index the index.
586: * @return The index.
587: */
588: public String getEncoding(final int index) {
589: final EncodingCarrier ec = (EncodingCarrier) encodings
590: .get(index);
591: return ec.getName();
592: }
593:
594: /**
595: * Returns a description.
596: *
597: * @param index the index.
598: * @return The description.
599: */
600: public String getDescription(final int index) {
601: final EncodingCarrier ec = (EncodingCarrier) encodings
602: .get(index);
603: return ec.getDescription();
604: }
605:
606: /**
607: * Defines the loader settings for the available encodings shown to the user. The
608: * property defaults to AVAILABLE_ENCODINGS_ALL.
609: *
610: * @return either AVAILABLE_ENCODINGS_ALL, AVAILABLE_ENCODINGS_FILE or
611: * AVAILABLE_ENCODINGS_NONE.
612: */
613: public static String getEncodingsDefinitionFile() {
614: return JFreeReportBoot.getInstance().getGlobalConfig()
615: .getConfigProperty(ENCODINGS_DEFINITION_FILE,
616: ENCODINGS_DEFINITION_FILE_DEFAULT);
617: }
618:
619: /**
620: * Defines the loader settings for the available encodings shown to the user. The
621: * property defaults to AVAILABLE_ENCODINGS_ALL.
622: *
623: * @return either AVAILABLE_ENCODINGS_ALL, AVAILABLE_ENCODINGS_FILE or
624: * AVAILABLE_ENCODINGS_NONE.
625: */
626: public static String getAvailableEncodings() {
627: return JFreeReportBoot.getInstance().getGlobalConfig()
628: .getConfigProperty(AVAILABLE_ENCODINGS,
629: AVAILABLE_ENCODINGS_ALL);
630: }
631:
632: public void setSelectedEncoding(final String encoding) {
633: if (encoding == null) {
634: throw new NullPointerException();
635: }
636:
637: final int size = encodings.size();
638: for (int i = 0; i < size; i++) {
639: final EncodingCarrier carrier = (EncodingCarrier) encodings
640: .get(i);
641: if (encoding.equals(carrier.getName())) {
642: selectedIndex = i;
643: selectedObject = carrier.getDisplayName();
644: fireContentsChanged(-1, -1);
645: return;
646: }
647: }
648: // default fall-back to have a valid value ..
649: if (size > 0) {
650: selectedIndex = 0;
651: selectedObject = getElementAt(0);
652: fireContentsChanged(-1, -1);
653: }
654: }
655: }
|