001/** 002 * Copyright 2003 Sun Microsystems, Inc. 003 * 004 * See the file "license.terms" for information on usage and 005 * redistribution of this file, and for a DISCLAIMER OF ALL 006 * WARRANTIES. 007 */ 008package com.sun.speech.freetts; 009 010import java.io.BufferedReader; 011import java.io.File; 012import java.io.FileInputStream; 013import java.io.FileNotFoundException; 014import java.io.IOException; 015import java.io.InputStream; 016import java.io.InputStreamReader; 017import java.net.JarURLConnection; 018import java.net.MalformedURLException; 019import java.net.URI; 020import java.net.URL; 021import java.net.URLClassLoader; 022import java.util.Collection; 023import java.util.jar.Attributes; 024 025/** 026 * Provides access to voices for all of FreeTTS. There is only one instance of 027 * the VoiceManager. 028 * 029 * Each call to getVoices() creates a new instance of each voice. 030 * 031 * @see Voice 032 * @see VoiceDirectory 033 */ 034public class VoiceManager { 035 036 private static final VoiceManager INSTANCE; 037 038 private static final String PATH_SEPARATOR ; 039 040 /** 041 * we only want one class loader, otherwise the static information for 042 * loaded classes would be duplicated for each class loader 043 */ 044 private static final DynamicClassLoader classLoader; 045 046 static { 047 PATH_SEPARATOR = System.getProperty("path.separator"); 048 INSTANCE = new VoiceManager(); 049 final ClassLoader parent = VoiceManager.class.getClassLoader(); 050 classLoader = new DynamicClassLoader(new URL[0], parent); 051 } 052 053 /** 054 * Do not allow creation from outside. 055 */ 056 private VoiceManager() { 057 } 058 059 /** 060 * Gets the instance of the VoiceManager 061 * 062 * @return a VoiceManager 063 */ 064 public static VoiceManager getInstance() { 065 return INSTANCE; 066 } 067 068 /** 069 * Provide an array of all voices available to FreeTTS. 070 * 071 * First, if the "freetts.voices" property is set, it is assumed to be a 072 * comma-separated list of VoiceDirectory classnames (e.g., 073 * "-Dfreetts.voices=com.sun.speech.freetts.en.us.cmu_us_kal.KevinVoiceDirectory" 074 * ). If this property exists, the VoiceManager will use only this property 075 * to find voices -- no other method described below will be used. The 076 * primary purpose for this property is testing and for use with WebStart. 077 * 078 * <p> 079 * Second, the file internal_voices.txt is looked for in the same directory 080 * as VoiceManager.class. If the file does not exist, the VoiceManager moves 081 * on. Next, it looks for voices.txt in the same directory as freetts.jar. 082 * If the file does not exist, the VoiceManager moves on. Next, if the 083 * property "freetts.voicesfile" is defined, then that file is read in. If 084 * the property is defined and the file does not exist, then an error is 085 * raised. 086 * 087 * <P> 088 * Every voices file that is read in contains a list of VoiceDirectory class 089 * names. 090 * 091 * <p> 092 * Next, the voice manager looks for freetts voice jarfiles that may exist 093 * in well-known locations. The directory that contains freetts.jar is 094 * searched for voice jarfiles, then directories specified by the 095 * "freetts.voicespath" system property. Any jarfile whose Manifest contains 096 * "FreeTTSVoiceDefinition: true" is assumed to be a FreeTTS voice, and the 097 * Manifest's "Main-Class" entry is assumed to be the name of the voice 098 * directory. The dependencies of the voice jarfiles specified by the 099 * "Class-Path" Manifest entry are also loaded. 100 * 101 * <p> 102 * The VoiceManager instantiates each voice directory and calls getVoices() 103 * on each. 104 * 105 * @return the array of new instances of all available voices 106 */ 107 public Voice[] getVoices() { 108 final UniqueVector<Voice> voices = new UniqueVector<Voice>(); 109 final Collection<VoiceDirectory> voiceDirectories; 110 try { 111 voiceDirectories = getVoiceDirectories(); 112 } catch (IOException e) { 113 throw new Error(e.getMessage(), e); 114 } 115 for (VoiceDirectory dir : voiceDirectories) { 116 voices.addArray(dir.getVoices()); 117 } 118 119 Voice[] voiceArray = new Voice[voices.size()]; 120 return (Voice[]) voices.toArray(voiceArray); 121 } 122 123 /** 124 * Prints detailed information about all available voices. 125 * 126 * @return a String containing the information 127 */ 128 public String getVoiceInfo() { 129 StringBuilder infoString = new StringBuilder(); 130 Collection<VoiceDirectory> voiceDirectories; 131 try { 132 voiceDirectories = getVoiceDirectories(); 133 } catch (IOException e) { 134 throw new Error(e.getMessage(), e); 135 } 136 for( VoiceDirectory dir : voiceDirectories) { 137 infoString.append(dir.toString()); 138 } 139 return infoString.toString(); 140 } 141 142 /** 143 * Creates an array of all voice directories of all available voices using 144 * the criteria specified by the contract for getVoices(). 145 * 146 * @return the voice directories 147 * @see getVoices() 148 * @exception IOException 149 * error loading a voice directory 150 */ 151 private Collection<VoiceDirectory> getVoiceDirectories() 152 throws IOException { 153 try { 154 // If there is a freetts.voices property, it means two 155 // things: 1) it is a comma separated list of class names 156 // 2) no other attempts to find voices should be 157 // made 158 // 159 // The main purpose for this property is to allow for 160 // voices to be found via WebStart. 161 // 162 String voiceClasses = System.getProperty("freetts.voices"); 163 if (voiceClasses != null) { 164 return getVoiceDirectoryNamesFromProperty(voiceClasses); 165 } 166 167 // Get voice directory names from voices files 168 UniqueVector<String> voiceDirectoryNames = 169 getVoiceDirectoryNamesFromFiles(); 170 171 // Get list of voice jars 172 UniqueVector<URL> pathURLs = getVoiceJarURLs(); 173 voiceDirectoryNames.addVector( 174 getVoiceDirectoryNamesFromJarURLs(pathURLs)); 175 176 // Get dependencies 177 // Copy of vector made because vector may be modified by 178 // each call to getDependencyURLs 179 URL[] voiceJarURLs = (URL[]) pathURLs.toArray( 180 new URL[pathURLs.size()]); 181 for (int i = 0; i < voiceJarURLs.length; i++) { 182 getDependencyURLs(voiceJarURLs[i], pathURLs); 183 } 184 185 // If the voice jars have already been added to the classpath 186 // we avoid to add them a second time. 187 boolean noexpansion = Boolean.getBoolean("freetts.nocpexpansion"); 188 if (!noexpansion) { 189 // Extend class path 190 for (int i = 0; i < pathURLs.size(); i++) { 191 classLoader.addUniqueURL((URL) pathURLs.get(i)); 192 } 193 } 194 195 // Create an instance of each voice directory 196 UniqueVector<VoiceDirectory> voiceDirectories = 197 new UniqueVector<VoiceDirectory>(); 198 for (int i = 0; i < voiceDirectoryNames.size(); i++) { 199 @SuppressWarnings("unchecked") 200 Class<VoiceDirectory> c = 201 (Class<VoiceDirectory>) Class.forName( 202 (String) voiceDirectoryNames.get(i), 203 true, classLoader); 204 voiceDirectories.add(c.newInstance()); 205 } 206 207 return voiceDirectories.elements(); 208 } catch (InstantiationException e) { 209 throw new Error("Unable to load voice directory. " + e); 210 } catch (ClassNotFoundException e) { 211 throw new Error("Unable to load voice directory. " + e); 212 } catch (IllegalAccessException e) { 213 throw new Error("Unable to load voice directory. " + e); 214 } 215 216 } 217 218 /** 219 * Gets VoiceDirectory instances by parsing a comma separated String of 220 * VoiceDirectory class names. 221 */ 222 private Collection<VoiceDirectory> getVoiceDirectoryNamesFromProperty( 223 String voiceClasses) throws InstantiationException, 224 IllegalAccessException, ClassNotFoundException { 225 226 String[] classnames = voiceClasses.split(","); 227 228 Collection<VoiceDirectory> directories = 229 new java.util.ArrayList<VoiceDirectory>(); 230 231 for (int i = 0; i < classnames.length; i++) { 232 @SuppressWarnings("unchecked") 233 Class<VoiceDirectory> c = 234 (Class<VoiceDirectory>) classLoader.loadClass(classnames[i]); 235 directories.add(c.newInstance()); 236 } 237 238 return directories; 239 } 240 241 /** 242 * Recursively gets the urls of the class paths that url is dependant on. 243 * 244 * Conventions specified in 245 * http://java.sun.com/j2se/1.4.1/docs/guide/extensions/spec.html#bundled 246 * are followed. 247 * 248 * @param url 249 * the url to recursively check. If it ends with a "/" then it is 250 * presumed to be a directory, and is not checked. Otherwise it 251 * is assumed to be a jar, and its manifest is read to get the 252 * urls Class-Path entry. These urls are passed to this method 253 * recursively. 254 * 255 * @param dependencyURLs 256 * a vector containing all of the dependent urls found. This 257 * parameter is modified as urls are added to it. 258 * @exception IOException 259 * error openig the URL connection 260 */ 261 private void getDependencyURLs(URL url, UniqueVector<URL> dependencyURLs) 262 throws IOException { 263 String urlDirName = getURLDirName(url); 264 if (url.getProtocol().equals("jar")) { // only check deps of jars 265 266 // read in Class-Path attribute of jar Manifest 267 JarURLConnection jarConnection = (JarURLConnection) url 268 .openConnection(); 269 Attributes attributes = jarConnection.getMainAttributes(); 270 String fullClassPath = attributes 271 .getValue(Attributes.Name.CLASS_PATH); 272 if (fullClassPath == null || fullClassPath.equals("")) { 273 return; // no classpaths to add 274 } 275 276 // The URLs are separated by one or more spaces 277 String[] classPath = fullClassPath.split("\\s+"); 278 URL classPathURL; 279 for (int i = 0; i < classPath.length; i++) { 280 try { 281 if (classPath[i].endsWith("/")) { // assume directory 282 classPathURL = new URL("file:" + urlDirName 283 + classPath[i]); 284 } else { // assume jar 285 classPathURL = new URL("jar", "", "file:" 286 + urlDirName + classPath[i] + "!/"); 287 } 288 } catch (MalformedURLException e) { 289 System.err 290 .println("Warning: unable to resolve dependency " 291 + classPath[i] 292 + " referenced by " 293 + url); 294 continue; 295 } 296 297 // don't get in a recursive loop if two jars 298 // are mutually dependant 299 if (!dependencyURLs.contains(classPathURL)) { 300 dependencyURLs.add(classPathURL); 301 getDependencyURLs(classPathURL, dependencyURLs); 302 } 303 } 304 } 305 } 306 307 /** 308 * Gets the names of the subclasses of VoiceDirectory that are listed in the 309 * voices.txt files. 310 * 311 * @return a vector containing the String names of the voice directories 312 * @exception IOException 313 * error reading voice files. 314 */ 315 private UniqueVector<String> getVoiceDirectoryNamesFromFiles() 316 throws IOException { 317 final UniqueVector<String> voiceDirectoryNames = 318 new UniqueVector<String>(); 319 320 // first, load internal_voices.txt 321 InputStream is = this.getClass().getResourceAsStream( 322 "internal_voices.txt"); 323 if (is != null) { // if it doesn't exist, move on 324 voiceDirectoryNames.addVector(getVoiceDirectoryNamesFromInputStream( 325 is)); 326 } 327 328 // next, try loading voices.txt 329 try { 330 voiceDirectoryNames 331 .addVector(getVoiceDirectoryNamesFromFile(getBaseDirectory() 332 + "voices.txt")); 333 } catch (FileNotFoundException e) { 334 // do nothing 335 } catch (IOException e) { 336 // do nothing 337 } 338 339 // last, read voices from property freetts.voicesfile 340 String voicesFile = System.getProperty("freetts.voicesfile"); 341 if (voicesFile != null) { 342 voiceDirectoryNames.addVector( 343 getVoiceDirectoryNamesFromFile(voicesFile)); 344 } 345 346 return voiceDirectoryNames; 347 } 348 349 /** 350 * Gets the voice directory class names from a list of urls specifying voice 351 * jarfiles. The class name is specified as the Main-Class in the manifest 352 * of the jarfiles. 353 * 354 * @param urls 355 * a UniqueVector of URLs that refer to the voice jarfiles 356 * 357 * @return a UniqueVector of Strings representing the voice directory class 358 * names 359 */ 360 private UniqueVector<String> getVoiceDirectoryNamesFromJarURLs( 361 UniqueVector<URL> urls) { 362 try { 363 UniqueVector<String> voiceDirectoryNames = 364 new UniqueVector<String>(); 365 for (int i = 0; i < urls.size(); i++) { 366 JarURLConnection jarConnection = (JarURLConnection) ((URL) urls 367 .get(i)).openConnection(); 368 Attributes attributes = jarConnection.getMainAttributes(); 369 String mainClass = attributes 370 .getValue(Attributes.Name.MAIN_CLASS); 371 if (mainClass == null || mainClass.trim().equals("")) { 372 throw new Error("No Main-Class found in jar " 373 + (URL) urls.get(i)); 374 } 375 376 voiceDirectoryNames.add(mainClass); 377 } 378 return voiceDirectoryNames; 379 } catch (IOException e) { 380 throw new Error("Error reading jarfile manifests."); 381 } 382 } 383 384 /** 385 * Gets the list of voice jarfiles. Voice jarfiles are searched for in the 386 * same directory as freetts.jar and the directories specified by the 387 * freetts.voicespath system property. Voice jarfiles are defined by the 388 * manifest entry "FreeTTSVoiceDefinition: true" 389 * 390 * @return a vector of URLs refering to the voice jarfiles. 391 */ 392 private UniqueVector<URL> getVoiceJarURLs() { 393 UniqueVector<URL> voiceJarURLs = new UniqueVector<URL>(); 394 395 // check in same directory as freetts.jar 396 try { 397 String baseDirectory = getBaseDirectory(); 398 if (!baseDirectory.equals("")) { // not called from a jar 399 voiceJarURLs.addVector(getVoiceJarURLsFromDir(baseDirectory)); 400 } 401 } catch (FileNotFoundException e) { 402 // do nothing 403 } 404 405 // search voicespath 406 String voicesPath = System.getProperty("freetts.voicespath", ""); 407 if (!voicesPath.equals("")) { 408 String[] dirNames = voicesPath.split(PATH_SEPARATOR); 409 for (int i = 0; i < dirNames.length; i++) { 410 try { 411 voiceJarURLs.addVector(getVoiceJarURLsFromDir(dirNames[i])); 412 } catch (FileNotFoundException e) { 413 throw new Error("Error loading jars from voicespath " 414 + dirNames[i] + ". "); 415 } 416 } 417 } 418 419 return voiceJarURLs; 420 } 421 422 /** 423 * Gets the list of voice jarfiles in a specific directory. 424 * 425 * @return a vector of URLs refering to the voice jarfiles 426 * @see getVoiceJarURLs() 427 */ 428 private UniqueVector<URL> getVoiceJarURLsFromDir(String dirName) 429 throws FileNotFoundException { 430 try { 431 UniqueVector<URL> voiceJarURLs = new UniqueVector<URL>(); 432 File dir = new File(new URI("file://" + dirName)); 433 if (!dir.isDirectory()) { 434 throw new FileNotFoundException("File is not a directory: " 435 + dirName); 436 } 437 File[] files = dir.listFiles(); 438 for (int i = 0; i < files.length; i++) { 439 File file = files[i]; 440 if (file.isFile() && (!file.isHidden()) 441 && file.getName().endsWith(".jar")) { 442 URL jarURL = file.toURI().toURL(); 443 jarURL = new URL("jar", "", "file:" + jarURL.getPath() 444 + "!/"); 445 JarURLConnection jarConnection = (JarURLConnection) jarURL 446 .openConnection(); 447 // if it is not a real jar file, we will end up 448 // with a null set of attributes. 449 450 Attributes attributes = jarConnection.getMainAttributes(); 451 if (attributes != null) { 452 String isVoice = attributes 453 .getValue("FreeTTSVoiceDefinition"); 454 if (isVoice != null && isVoice.trim().equals("true")) { 455 voiceJarURLs.add(jarURL); 456 } 457 } 458 } 459 } 460 return voiceJarURLs; 461 } catch (java.net.URISyntaxException e) { 462 throw new Error("Error reading directory name '" + dirName + "'."); 463 } catch (MalformedURLException e) { 464 throw new Error("Error reading jars from directory " + dirName 465 + ". "); 466 } catch (IOException e) { 467 throw new Error("Error reading jars from directory " + dirName 468 + ". "); 469 } 470 } 471 472 /** 473 * Provides a string representation of all voices available to FreeTTS. 474 * 475 * @return a String which is a space-delimited list of voice names. If there 476 * is more than one voice, then the word "or" appears before the 477 * last one. 478 */ 479 public String toString() { 480 StringBuilder names = new StringBuilder(); 481 Voice[] voices = getVoices(); 482 for (int i = 0; i < voices.length; i++) { 483 if (i == voices.length - 1) { 484 if (i == 0) { 485 names.append(voices[i].getName()); 486 } else { 487 names.append("or "); 488 names.append(voices[i].getName()); 489 } 490 } else { 491 names.append(voices[i].getName()); 492 names.append(" "); 493 } 494 } 495 return names.toString(); 496 } 497 498 /** 499 * Check if there is a voice provides with the given name. 500 * 501 * @param voiceName 502 * the name of the voice to check 503 * 504 * @return <b>true</b> if FreeTTS has a voice available with the name 505 * <b>voiceName</b>, else <b>false</b>. 506 */ 507 public boolean contains(String voiceName) { 508 return (getVoice(voiceName) != null); 509 } 510 511 /** 512 * Get a Voice with a given name. 513 * 514 * @param voiceName 515 * the name of the voice to get. 516 * 517 * @return the Voice that has the same name as <b>voiceName</b> if one 518 * exists, else <b>null</b> 519 */ 520 public Voice getVoice(String voiceName) { 521 Voice[] voices = getVoices(); 522 for (int i = 0; i < voices.length; i++) { 523 if (voices[i].getName().equals(voiceName)) { 524 return voices[i]; 525 } 526 } 527 return null; 528 } 529 530 /** 531 * Get the directory that the jar file containing this class resides in. 532 * 533 * @return the name of the directory with a trailing "/" (or equivalent for 534 * the particular operating system), or "" if unable to determin. 535 * (For example this class does not reside inside a jar file). 536 */ 537 private String getBaseDirectory() { 538 String name = this.getClass().getName(); 539 int lastdot = name.lastIndexOf('.'); 540 if (lastdot != -1) { // remove package information 541 name = name.substring(lastdot + 1); 542 } 543 544 URL url = this.getClass().getResource(name + ".class"); 545 return getURLDirName(url); 546 } 547 548 /** 549 * Gets the directory name from a URL 550 * 551 * @param url 552 * the url to parse 553 * @return the String representation of the directory name in a URL 554 */ 555 private String getURLDirName(URL url) { 556 String urlFileName = url.getPath(); 557 int i = urlFileName.lastIndexOf('!'); 558 if (i == -1) { 559 i = urlFileName.length(); 560 } 561 int dir = urlFileName.lastIndexOf("/", i); 562 if (!urlFileName.startsWith("file:")) { 563 return ""; 564 } 565 return urlFileName.substring(5, dir) + "/"; 566 } 567 568 /** 569 * Get the names of the voice directories from a voices file. Blank lines 570 * and lines beginning with "#" are ignored. Beginning and trailing 571 * whitespace is ignored. 572 * 573 * @param fileName 574 * the name of the voices file to read from 575 * 576 * @return a vector of the names of the VoiceDirectory subclasses 577 * @throws FileNotFoundException 578 * @throws IOException 579 */ 580 private UniqueVector<String> getVoiceDirectoryNamesFromFile(String fileName) 581 throws FileNotFoundException, IOException { 582 InputStream is = new FileInputStream(fileName); 583 if (is == null) { 584 throw new IOException(); 585 } else { 586 return getVoiceDirectoryNamesFromInputStream(is); 587 } 588 } 589 590 /** 591 * Get the names of the voice directories from an input stream. Blank lines 592 * and lines beginning with "#" are ignored. Beginning and trailing 593 * whitespace is ignored. 594 * 595 * @param is 596 * the input stream to read from 597 * 598 * @return a vector of the names of the VoiceDirectory subclasses 599 * @throws IOException 600 * error reading from the input stream 601 */ 602 private UniqueVector<String> getVoiceDirectoryNamesFromInputStream( 603 InputStream is) throws IOException { 604 final UniqueVector<String> names = new UniqueVector<String>(); 605 BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 606 while (true) { 607 String line = reader.readLine(); 608 if (line == null) { 609 break; 610 } 611 line = line.trim(); 612 if (!line.startsWith("#") && !line.equals("")) { 613 names.add(line); 614 } 615 } 616 return names; 617 } 618 619 /** 620 * Gets the class loader used for loading dynamically detected jars. This is 621 * useful to get resources out of jars that may be in the class path of this 622 * class loader but not in the class path of the system class loader. 623 * 624 * @return the class loader 625 */ 626 public static URLClassLoader getVoiceClassLoader() { 627 return classLoader; 628 } 629} 630 631/** 632 * The DynamicClassLoader provides a means to add urls to the classpath after 633 * the class loader has already been instantiated. 634 */ 635class DynamicClassLoader extends URLClassLoader { 636 637 private java.util.HashSet<URL> classPath; 638 639 /** 640 * Constructs a new URLClassLoader for the given URLs. The URLs will be 641 * searched in the order specified for classes and resources after first 642 * searching in the specified parent class loader. Any URL that ends with a 643 * '/' is assumed to refer to a directory. Otherwise, the URL is assumed to 644 * refer to a JAR file which will be downloaded and opened as needed. 645 * 646 * If there is a security manager, this method first calls the security 647 * manager's checkCreateClassLoader method to ensure creation of a class 648 * loader is allowed. 649 * 650 * @param urls 651 * the URLs from which to load classes and resources 652 * @param parent 653 * the parent class loader for delegation 654 * 655 * @throws SecurityException 656 * if a security manager exists and its checkCreateClassLoader 657 * method doesn't allow creation of a class loader. 658 */ 659 public DynamicClassLoader(URL[] urls, ClassLoader parent) { 660 super(urls, parent); 661 classPath = new java.util.HashSet<URL>(urls.length); 662 for (int i = 0; i < urls.length; i++) { 663 classPath.add(urls[i]); 664 } 665 } 666 667 /** 668 * Add a URL to a class path only if has not already been added. 669 * 670 * @param url 671 * the url to add to the class path 672 */ 673 public synchronized void addUniqueURL(final URL url) { 674 // Avoid loading of the freetts.jar. 675 final String name= url.toString(); 676 if (!classPath.contains(url) && (name.indexOf("freetts.jar") < 0)) { 677 super.addURL(url); 678 classPath.add(url); 679 } 680 } 681 682 /** 683 * {@inheritDoc} 684 */ 685 public Class<?> loadClass(final String name) 686 throws ClassNotFoundException { 687 Class<?> loadedClass = findLoadedClass(name); 688 if (loadedClass == null) { 689 try { 690 loadedClass = findClass(name); 691 } catch (ClassNotFoundException e) { 692 // Swallow exception 693 // does not exist locally 694 } 695 if (loadedClass == null) { 696 loadedClass = super.loadClass(name); 697 } 698 } 699 return loadedClass; 700 } 701} 702 703/** 704 * Provides a vector whose elements are always unique. The advantage over a Set 705 * is that the elements are still ordered in the way they were added. If an 706 * element is added that already exists, then nothing happens. 707 */ 708class UniqueVector<T> { 709 private java.util.HashSet<T> elementSet; 710 private java.util.Vector<T> elementVector; 711 712 /** 713 * Creates a new vector 714 */ 715 public UniqueVector() { 716 elementSet = new java.util.HashSet<T>(); 717 elementVector = new java.util.Vector<T>(); 718 } 719 720 /** 721 * Add an object o to the vector if it is not already present as defined by 722 * the function HashSet.contains(o) 723 * 724 * @param o 725 * the object to add 726 */ 727 public void add(T o) { 728 if (!contains(o)) { 729 elementSet.add(o); 730 elementVector.add(o); 731 } 732 } 733 734 /** 735 * Appends all elements of a vector to this vector. Only unique elements are 736 * added. 737 * 738 * @param v 739 * the vector to add 740 */ 741 public void addVector(UniqueVector<T> v) { 742 for (int i = 0; i < v.size(); i++) { 743 add(v.get(i)); 744 } 745 } 746 747 /** 748 * Appends all elements of an array to this vector. Only unique elements are 749 * added. 750 * 751 * @param a 752 * the array to add 753 */ 754 public void addArray(T[] a) { 755 for (int i = 0; i < a.length; i++) { 756 add(a[i]); 757 } 758 } 759 760 /** 761 * Gets the number of elements currently in vector. 762 * 763 * @return the number of elements in vector 764 */ 765 public int size() { 766 return elementVector.size(); 767 } 768 769 /** 770 * Checks if an element is present in the vector. The check follows the 771 * convention of HashSet contains() function, so performance can be expected 772 * to be a constant factor. 773 * 774 * @param o 775 * the object to check 776 * 777 * @return true if element o exists in the vector, else false. 778 */ 779 public boolean contains(T o) { 780 return elementSet.contains(o); 781 } 782 783 /** 784 * Gets an element from a vector. 785 * 786 * @param index 787 * the index into the vector from which to retrieve the element 788 * 789 * @return the object at index <b>index</b> 790 */ 791 public T get(int index) { 792 return elementVector.get(index); 793 } 794 795 /** 796 * Creates an array of the elements in the vector. Follows conventions of 797 * Vector.toArray(). 798 * 799 * @return an array representation of the object 800 */ 801 @SuppressWarnings("unchecked") 802 public T[] toArray() { 803 return (T[]) elementVector.toArray(); 804 } 805 806 /** 807 * Creates an array of the elements in the vector. Follows conventions of 808 * Vector.toArray(Object[]). 809 * 810 * @return an array representation of the object 811 */ 812 public T[] toArray(T[] a) { 813 return elementVector.toArray(a); 814 } 815 816 /** 817 * Returns the entries of this vector. 818 * @return elements. 819 */ 820 public Collection<T> elements() { 821 return elementVector; 822 } 823} 824