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 org.relaxng.datatype.helpers;
35 
36 import org.relaxng.datatype.DatatypeLibraryFactory;
37 import 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 
createDatatypeLibrary(String uri)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 
hasMoreElements()81             public boolean hasMoreElements() {
82               return nextIndex < providers.size() || moreProviders();
83             }
84 
nextElement()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;
Singleton(Object obj)97             private Singleton(Object obj) {
98               this.obj = obj;
99             }
100 
hasMoreElements()101             public boolean hasMoreElements() {
102               return obj != null;
103             }
104 
nextElement()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 {
getResources(String resName)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 
loadClass(String name)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 
Loader2()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 
getResources(String resName)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 
loadClass(String name)158             Class loadClass(String name) throws ClassNotFoundException {
159               return Class.forName(name, true, cl);
160             }
161           }
162 
Service(Class cls)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 
getProviders()175           public Enumeration getProviders() {
176             return new ProviderEnumeration();
177           }
178 
moreProviders()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 
parseConfigFile(URL url)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