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 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 }