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 3525 2007-10-16 11:43:48Z tmorgner $ 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 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 }