1 /**
   2  * Copyright (c) 2001, Thai Open Source Software Center Ltd
   3  * All rights reserved.
   4  *
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions are
   7  * met:
   8  *
   9  *     Redistributions of source code must retain the above copyright
  10  *     notice, this list of conditions and the following disclaimer.
  11  *
  12  *     Redistributions in binary form must reproduce the above copyright
  13  *     notice, this list of conditions and the following disclaimer in
  14  *     the documentation and/or other materials provided with the
  15  *     distribution.
  16  *
  17  *     Neither the name of the Thai Open Source Software Center Ltd nor
  18  *     the names of its contributors may be used to endorse or promote
  19  *     products derived from this software without specific prior written
  20  *     permission.
  21  *
  22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  25  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
  26  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  27  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  29  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  31  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  32  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33  */
  34 package com.sun.xml.internal.org.relaxng.datatype.helpers;
  35 
  36 import com.sun.xml.internal.org.relaxng.datatype.DatatypeLibraryFactory;
  37 import com.sun.xml.internal.org.relaxng.datatype.DatatypeLibrary;
  38 import java.util.Enumeration;
  39 import java.util.NoSuchElementException;
  40 import java.util.Vector;
  41 import java.io.Reader;
  42 import java.io.InputStream;
  43 import java.io.InputStreamReader;
  44 import java.io.BufferedReader;
  45 import java.io.IOException;
  46 import java.io.UnsupportedEncodingException;
  47 import java.net.URL;
  48 
  49 /**
  50  * Discovers the datatype library implementation from the classpath.
  51  *
  52  * <p>
  53  * The call of the createDatatypeLibrary method finds an implementation
  54  * from a given datatype library URI at run-time.
  55  */
  56 public class DatatypeLibraryLoader implements DatatypeLibraryFactory {
  57   private final Service service = new Service(DatatypeLibraryFactory.class);
  58 
  59   public DatatypeLibrary createDatatypeLibrary(String uri) {
  60     for (Enumeration e = service.getProviders();
  61          e.hasMoreElements();) {
  62       DatatypeLibraryFactory factory
  63         = (DatatypeLibraryFactory)e.nextElement();
  64       DatatypeLibrary library = factory.createDatatypeLibrary(uri);
  65       if (library != null)
  66         return library;
  67     }
  68     return null;
  69   }
  70 
  71         private static class Service {
  72           private final Class serviceClass;
  73           private final Enumeration configFiles;
  74           private Enumeration classNames = null;
  75           private final Vector providers = new Vector();
  76           private Loader loader;
  77 
  78           private class ProviderEnumeration implements Enumeration {
  79             private int nextIndex = 0;
  80 
  81             public boolean hasMoreElements() {
  82               return nextIndex < providers.size() || moreProviders();
  83             }
  84 
  85             public Object nextElement() {
  86               try {
  87                 return providers.elementAt(nextIndex++);
  88               }
  89               catch (ArrayIndexOutOfBoundsException e) {
  90                 throw new NoSuchElementException();
  91               }
  92             }
  93           }
  94 
  95           private static class Singleton implements Enumeration {
  96             private Object obj;
  97             private Singleton(Object obj) {
  98               this.obj = obj;
  99             }
 100 
 101             public boolean hasMoreElements() {
 102               return obj != null;
 103             }
 104 
 105             public Object nextElement() {
 106               if (obj == null)
 107                 throw new NoSuchElementException();
 108               Object tem = obj;
 109               obj = null;
 110               return tem;
 111             }
 112           }
 113 
 114           // JDK 1.1
 115           private static class Loader {
 116             Enumeration getResources(String resName) {
 117               ClassLoader cl = Loader.class.getClassLoader();
 118               URL url;
 119               if (cl == null)
 120                 url = ClassLoader.getSystemResource(resName);
 121               else
 122                 url = cl.getResource(resName);
 123               return new Singleton(url);
 124             }
 125 
 126             Class loadClass(String name) throws ClassNotFoundException {
 127               return Class.forName(name);
 128             }
 129           }
 130 
 131           // JDK 1.2+
 132           private static class Loader2 extends Loader {
 133             private ClassLoader cl;
 134 
 135             Loader2() {
 136               cl = Loader2.class.getClassLoader();
 137               // If the thread context class loader has the class loader
 138               // of this class as an ancestor, use the thread context class
 139               // loader.  Otherwise, the thread context class loader
 140               // probably hasn't been set up properly, so don't use it.
 141               ClassLoader clt = Thread.currentThread().getContextClassLoader();
 142               for (ClassLoader tem = clt; tem != null; tem = tem.getParent())
 143                 if (tem == cl) {
 144                   cl = clt;
 145                   break;
 146                 }
 147             }
 148 
 149             Enumeration getResources(String resName) {
 150               try {
 151                 return cl.getResources(resName);
 152               }
 153               catch (IOException e) {
 154                 return new Singleton(null);
 155               }
 156             }
 157 
 158             Class loadClass(String name) throws ClassNotFoundException {
 159               return Class.forName(name, true, cl);
 160             }
 161           }
 162 
 163           public Service(Class cls) {
 164             try {
 165               loader = new Loader2();
 166             }
 167             catch (NoSuchMethodError e) {
 168               loader = new Loader();
 169             }
 170             serviceClass = cls;
 171             String resName = "META-INF/services/" + serviceClass.getName();
 172             configFiles = loader.getResources(resName);
 173           }
 174 
 175           public Enumeration getProviders() {
 176             return new ProviderEnumeration();
 177           }
 178 
 179           synchronized private boolean moreProviders() {
 180             for (;;) {
 181               while (classNames == null) {
 182                 if (!configFiles.hasMoreElements())
 183                   return false;
 184                 classNames = parseConfigFile((URL)configFiles.nextElement());
 185               }
 186               while (classNames.hasMoreElements()) {
 187                 String className = (String)classNames.nextElement();
 188                 try {
 189                   Class cls = loader.loadClass(className);
 190                   Object obj = cls.newInstance();
 191                   if (serviceClass.isInstance(obj)) {
 192                     providers.addElement(obj);
 193                     return true;
 194                   }
 195                 }
 196                 catch (ClassNotFoundException e) { }
 197                 catch (InstantiationException e) { }
 198                 catch (IllegalAccessException e) { }
 199                 catch (LinkageError e) { }
 200               }
 201               classNames = null;
 202             }
 203           }
 204 
 205           private static final int START = 0;
 206           private static final int IN_NAME = 1;
 207           private static final int IN_COMMENT = 2;
 208 
 209           private static Enumeration parseConfigFile(URL url) {
 210             try {
 211               InputStream in = url.openStream();
 212               Reader r;
 213               try {
 214                 r = new InputStreamReader(in, "UTF-8");
 215               }
 216               catch (UnsupportedEncodingException e) {
 217                 r = new InputStreamReader(in, "UTF8");
 218               }
 219               r = new BufferedReader(r);
 220               Vector tokens = new Vector();
 221               StringBuffer tokenBuf = new StringBuffer();
 222               int state = START;
 223               for (;;) {
 224                 int n = r.read();
 225                 if (n < 0)
 226                   break;
 227                 char c = (char)n;
 228                 switch (c) {
 229                 case '\r':
 230                 case '\n':
 231                   state = START;
 232                   break;
 233                 case ' ':
 234                 case '\t':
 235                   break;
 236                 case '#':
 237                   state = IN_COMMENT;
 238                   break;
 239                 default:
 240                   if (state != IN_COMMENT) {
 241                     state = IN_NAME;
 242                     tokenBuf.append(c);
 243                   }
 244                   break;
 245                 }
 246                 if (tokenBuf.length() != 0 && state != IN_NAME) {
 247                   tokens.addElement(tokenBuf.toString());
 248                   tokenBuf.setLength(0);
 249                 }
 250               }
 251               if (tokenBuf.length() != 0)
 252                 tokens.addElement(tokenBuf.toString());
 253               return tokens.elements();
 254             }
 255             catch (IOException e) {
 256               return null;
 257             }
 258           }
 259         }
 260 
 261 }