Source for org.jfree.report.modules.gui.swing.common.EncodingComboBoxModel

   1: /**
   2:  * ========================================
   3:  * JFreeReport : a free Java report library
   4:  * ========================================
   5:  *
   6:  * Project Info:  http://reporting.pentaho.org/
   7:  *
   8:  * (C) Copyright 2000-2007, by Object Refinery Limited, Pentaho Corporation and Contributors.
   9:  *
  10:  * This library is free software; you can redistribute it and/or modify it under the terms
  11:  * of the GNU Lesser General Public License as published by the Free Software Foundation;
  12:  * either version 2.1 of the License, or (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  15:  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16:  * See the GNU Lesser General Public License for more details.
  17:  *
  18:  * You should have received a copy of the GNU Lesser General Public License along with this
  19:  * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  20:  * Boston, MA 02111-1307, USA.
  21:  *
  22:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  23:  * in the United States and other countries.]
  24:  *
  25:  * ------------
  26:  * $Id: EncodingComboBoxModel.java 3525 2007-10-16 11:43:48Z tmorgner $
  27:  * ------------
  28:  * (C) Copyright 2000-2005, by Object Refinery Limited.
  29:  * (C) Copyright 2005-2007, by Pentaho Corporation.
  30:  */
  31: package org.jfree.report.modules.gui.swing.common;
  32: 
  33: import java.io.BufferedInputStream;
  34: import java.io.IOException;
  35: import java.io.InputStream;
  36: import java.util.ArrayList;
  37: import java.util.Collections;
  38: import java.util.Comparator;
  39: import java.util.Enumeration;
  40: import java.util.Locale;
  41: import java.util.Properties;
  42: import java.util.ResourceBundle;
  43: import javax.swing.ComboBoxModel;
  44: import javax.swing.event.ListDataEvent;
  45: import javax.swing.event.ListDataListener;
  46: 
  47: import org.jfree.fonts.encoding.EncodingRegistry;
  48: import org.jfree.report.JFreeReportBoot;
  49: import org.jfree.util.Log;
  50: import org.jfree.util.ObjectUtilities;
  51: 
  52: /**
  53:  * A model for the 'encoding' combo box. This combobox model presents a selection for all
  54:  * available string encodings.
  55:  *
  56:  * @author Thomas Morgner.
  57:  */
  58: public class EncodingComboBoxModel implements ComboBoxModel
  59: {
  60:   /**
  61:    * A default description.
  62:    */
  63:   private static final String ENCODING_DEFAULT_DESCRIPTION =
  64:           "[no description]";
  65: 
  66:   /**
  67:    * The property that defines which encodings are available in the export dialogs.
  68:    */
  69:   public static final String AVAILABLE_ENCODINGS
  70:           = "org.jfree.report.modules.gui.base.EncodingsAvailable";
  71: 
  72:   /**
  73:    * The encodings available properties value for all properties.
  74:    */
  75:   public static final String AVAILABLE_ENCODINGS_ALL = "all";
  76:   /**
  77:    * The encodings available properties value for properties defined in the properties
  78:    * file.
  79:    */
  80:   public static final String AVAILABLE_ENCODINGS_FILE = "file";
  81:   /**
  82:    * The encodings available properties value for no properties defined. The encoding
  83:    * selection will be disabled.
  84:    */
  85:   public static final String AVAILABLE_ENCODINGS_NONE = "none";
  86: 
  87:   /**
  88:    * The name of the properties file used to define the available encodings. The property
  89:    * points to a resources in the classpath, not to a real file!
  90:    */
  91:   public static final String ENCODINGS_DEFINITION_FILE
  92:           = "org.jfree.report.modules.gui.base.EncodingsFile";
  93: 
  94:   /**
  95:    * The default name for the encoding properties file. This property defaults to
  96:    * "/org/jfree/report/jfreereport-encodings.properties".
  97:    */
  98:   public static final String ENCODINGS_DEFINITION_FILE_DEFAULT
  99:           = "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:     private 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:     private 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 (final 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 (final int start, final 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 (final 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 ("true".equalsIgnoreCase(encDef.getProperty(enc, "false")))
 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: }