/** | |
* Copyright (c) 2001, Thai Open Source Software Center Ltd | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are | |
* met: | |
* | |
* Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* | |
* Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in | |
* the documentation and/or other materials provided with the | |
* distribution. | |
* | |
* Neither the name of the Thai Open Source Software Center Ltd nor | |
* the names of its contributors may be used to endorse or promote | |
* products derived from this software without specific prior written | |
* permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR | |
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
package org.relaxng.datatype.helpers; | |
import org.relaxng.datatype.DatatypeLibraryFactory; | |
import org.relaxng.datatype.DatatypeLibrary; | |
import java.util.Enumeration; | |
import java.util.NoSuchElementException; | |
import java.util.Vector; | |
import java.io.Reader; | |
import java.io.InputStream; | |
import java.io.InputStreamReader; | |
import java.io.BufferedReader; | |
import java.io.IOException; | |
import java.io.UnsupportedEncodingException; | |
import java.net.URL; | |
/** | |
* Discovers the datatype library implementation from the classpath. | |
* | |
* <p> | |
* The call of the createDatatypeLibrary method finds an implementation | |
* from a given datatype library URI at run-time. | |
*/ | |
public class DatatypeLibraryLoader implements DatatypeLibraryFactory { | |
private final Service service = new Service(DatatypeLibraryFactory.class); | |
public DatatypeLibrary createDatatypeLibrary(String uri) { | |
for (Enumeration e = service.getProviders(); | |
e.hasMoreElements();) { | |
DatatypeLibraryFactory factory | |
= (DatatypeLibraryFactory)e.nextElement(); | |
DatatypeLibrary library = factory.createDatatypeLibrary(uri); | |
if (library != null) | |
return library; | |
} | |
return null; | |
} | |
private static class Service { | |
private final Class serviceClass; | |
private final Enumeration configFiles; | |
private Enumeration classNames = null; | |
private final Vector providers = new Vector(); | |
private Loader loader; | |
private class ProviderEnumeration implements Enumeration { | |
private int nextIndex = 0; | |
public boolean hasMoreElements() { | |
return nextIndex < providers.size() || moreProviders(); | |
} | |
public Object nextElement() { | |
try { | |
return providers.elementAt(nextIndex++); | |
} | |
catch (ArrayIndexOutOfBoundsException e) { | |
throw new NoSuchElementException(); | |
} | |
} | |
} | |
private static class Singleton implements Enumeration { | |
private Object obj; | |
private Singleton(Object obj) { | |
this.obj = obj; | |
} | |
public boolean hasMoreElements() { | |
return obj != null; | |
} | |
public Object nextElement() { | |
if (obj == null) | |
throw new NoSuchElementException(); | |
Object tem = obj; | |
obj = null; | |
return tem; | |
} | |
} | |
// JDK 1.1 | |
private static class Loader { | |
Enumeration getResources(String resName) { | |
ClassLoader cl = Loader.class.getClassLoader(); | |
URL url; | |
if (cl == null) | |
url = ClassLoader.getSystemResource(resName); | |
else | |
url = cl.getResource(resName); | |
return new Singleton(url); | |
} | |
Class loadClass(String name) throws ClassNotFoundException { | |
return Class.forName(name); | |
} | |
} | |
// JDK 1.2+ | |
private static class Loader2 extends Loader { | |
private ClassLoader cl; | |
Loader2() { | |
cl = Loader2.class.getClassLoader(); | |
// If the thread context class loader has the class loader | |
// of this class as an ancestor, use the thread context class | |
// loader. Otherwise, the thread context class loader | |
// probably hasn't been set up properly, so don't use it. | |
ClassLoader clt = Thread.currentThread().getContextClassLoader(); | |
for (ClassLoader tem = clt; tem != null; tem = tem.getParent()) | |
if (tem == cl) { | |
cl = clt; | |
break; | |
} | |
} | |
Enumeration getResources(String resName) { | |
try { | |
return cl.getResources(resName); | |
} | |
catch (IOException e) { | |
return new Singleton(null); | |
} | |
} | |
Class loadClass(String name) throws ClassNotFoundException { | |
return Class.forName(name, true, cl); | |
} | |
} | |
public Service(Class cls) { | |
try { | |
loader = new Loader2(); | |
} | |
catch (NoSuchMethodError e) { | |
loader = new Loader(); | |
} | |
serviceClass = cls; | |
String resName = "META-INF/services/" + serviceClass.getName(); | |
configFiles = loader.getResources(resName); | |
} | |
public Enumeration getProviders() { | |
return new ProviderEnumeration(); | |
} | |
synchronized private boolean moreProviders() { | |
for (;;) { | |
while (classNames == null) { | |
if (!configFiles.hasMoreElements()) | |
return false; | |
classNames = parseConfigFile((URL)configFiles.nextElement()); | |
} | |
while (classNames.hasMoreElements()) { | |
String className = (String)classNames.nextElement(); | |
try { | |
Class cls = loader.loadClass(className); | |
Object obj = cls.newInstance(); | |
if (serviceClass.isInstance(obj)) { | |
providers.addElement(obj); | |
return true; | |
} | |
} | |
catch (ClassNotFoundException e) { } | |
catch (InstantiationException e) { } | |
catch (IllegalAccessException e) { } | |
catch (LinkageError e) { } | |
} | |
classNames = null; | |
} | |
} | |
private static final int START = 0; | |
private static final int IN_NAME = 1; | |
private static final int IN_COMMENT = 2; | |
private static Enumeration parseConfigFile(URL url) { | |
try { | |
InputStream in = url.openStream(); | |
Reader r; | |
try { | |
r = new InputStreamReader(in, "UTF-8"); | |
} | |
catch (UnsupportedEncodingException e) { | |
r = new InputStreamReader(in, "UTF8"); | |
} | |
r = new BufferedReader(r); | |
Vector tokens = new Vector(); | |
StringBuffer tokenBuf = new StringBuffer(); | |
int state = START; | |
for (;;) { | |
int n = r.read(); | |
if (n < 0) | |
break; | |
char c = (char)n; | |
switch (c) { | |
case '\r': | |
case '\n': | |
state = START; | |
break; | |
case ' ': | |
case '\t': | |
break; | |
case '#': | |
state = IN_COMMENT; | |
break; | |
default: | |
if (state != IN_COMMENT) { | |
state = IN_NAME; | |
tokenBuf.append(c); | |
} | |
break; | |
} | |
if (tokenBuf.length() != 0 && state != IN_NAME) { | |
tokens.addElement(tokenBuf.toString()); | |
tokenBuf.setLength(0); | |
} | |
} | |
if (tokenBuf.length() != 0) | |
tokens.addElement(tokenBuf.toString()); | |
return tokens.elements(); | |
} | |
catch (IOException e) { | |
return null; | |
} | |
} | |
} | |
} | |