001/**
002 * Copyright (c) 2001, Thai Open Source Software Center Ltd
003 * All rights reserved.
004 * 
005 * Redistribution and use in source and binary forms, with or without
006 * modification, are permitted provided that the following conditions are
007 * met:
008 * 
009 *     Redistributions of source code must retain the above copyright
010 *     notice, this list of conditions and the following disclaimer.
011 * 
012 *     Redistributions in binary form must reproduce the above copyright
013 *     notice, this list of conditions and the following disclaimer in
014 *     the documentation and/or other materials provided with the
015 *     distribution.
016 * 
017 *     Neither the name of the Thai Open Source Software Center Ltd nor
018 *     the names of its contributors may be used to endorse or promote
019 *     products derived from this software without specific prior written
020 *     permission.
021 * 
022 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
023 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
024 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
025 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
026 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
027 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
028 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
029 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
030 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
031 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
032 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
033 */
034package org.relaxng.datatype.helpers;
035
036import org.relaxng.datatype.DatatypeLibraryFactory;
037import org.relaxng.datatype.DatatypeLibrary;
038import java.util.Enumeration;
039import java.util.NoSuchElementException;
040import java.util.Vector;
041import java.io.Reader;
042import java.io.InputStream;
043import java.io.InputStreamReader;
044import java.io.BufferedReader;
045import java.io.IOException;
046import java.io.UnsupportedEncodingException;
047import java.net.URL;
048
049/**
050 * Discovers the datatype library implementation from the classpath.
051 * 
052 * <p>
053 * The call of the createDatatypeLibrary method finds an implementation
054 * from a given datatype library URI at run-time.
055 */
056public class DatatypeLibraryLoader implements DatatypeLibraryFactory {
057  private final Service service = new Service(DatatypeLibraryFactory.class);
058
059  public DatatypeLibrary createDatatypeLibrary(String uri) {
060    for (Enumeration e = service.getProviders();
061         e.hasMoreElements();) {
062      DatatypeLibraryFactory factory
063        = (DatatypeLibraryFactory)e.nextElement();
064      DatatypeLibrary library = factory.createDatatypeLibrary(uri);
065      if (library != null)
066        return library;
067    }
068    return null;
069  }
070
071        private static class Service {
072          private final Class serviceClass;
073          private final Enumeration configFiles;
074          private Enumeration classNames = null;
075          private final Vector providers = new Vector();
076          private Loader loader;
077
078          private class ProviderEnumeration implements Enumeration {
079            private int nextIndex = 0;
080
081            public boolean hasMoreElements() {
082              return nextIndex < providers.size() || moreProviders();
083            }
084
085            public Object nextElement() {
086              try {
087                return providers.elementAt(nextIndex++);
088              }
089              catch (ArrayIndexOutOfBoundsException e) {
090                throw new NoSuchElementException();
091              }
092            }
093          }
094
095          private static class Singleton implements Enumeration {
096            private Object obj;
097            private Singleton(Object obj) {
098              this.obj = obj;
099            }
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}
262