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