/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.xenei.jdbc4sparql.sparql.builders;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.apache.commons.discovery.ResourceClassIterator;
import org.apache.commons.discovery.ResourceNameIterator;
import org.apache.commons.discovery.resource.ClassLoaders;
import org.apache.commons.discovery.resource.classes.DiscoverClasses;
import org.apache.commons.discovery.resource.names.DiscoverServiceNames;
import org.apache.commons.lang.StringUtils;
import org.xenei.jdbc4sparql.impl.rdf.RdfSchema;
import org.xenei.jdbc4sparql.impl.rdf.RdfTable;
/**
* An interface that defines classes that builds schemas.
* <p>
* Implementations of this interface should be listed in the
* META-INF/services/org.xenei.jdbc4sparql.sparql.builders.SchemaBuilder file.
* The first implementation listed in that file will be the default schema
* builder.
* </p>
* <p>
* Implementations should implement <code>
* public static final String BUILDER_NAME</code> and <code>
* public static final String DESCRIPTION </code>
* </p>
* <p>
* if the BUILDER_NAME is not specified the simple class name will be used. This
* is the name for the builder used in the J4S URL. This means that all
* characters used in the name should be easily typable into a URL without
* necessitating encoding. However, this is not a hard requirement and is not
* enforced.
* </p>
* <p>
* if two builders have the same name only the first one will be seen.
* </p>
* <p>
* A list of registered SchemaBuilders is returned from J4SDriver when it is run
* as a java application (e.g. java -jar J4DDriver.jar J4SDriver)
* </p>
*/
public interface SchemaBuilder {
public static class Util {
public static SchemaBuilder getBuilder(final String name) {
final List<Class<? extends SchemaBuilder>> lst = Util.getBuilders();
if (StringUtils.isEmpty(name)) {
try {
return lst.get(0).newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new IllegalStateException(lst.get(0)
+ " could not be instantiated.", e);
}
}
for (final Class<? extends SchemaBuilder> c : lst) {
if (Util.getName(c).equals(name)) {
try {
return c.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new IllegalStateException(c
+ " could not be instantiated.", e);
}
}
}
try {
final Class<?> clazz = Class.forName(name);
if (SchemaBuilder.class.isAssignableFrom(clazz)) {
try {
return (SchemaBuilder) clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new IllegalStateException(clazz
+ " could not be instantiated.", e);
}
}
else {
throw new IllegalArgumentException(clazz
+ " does not implement SchemaBuilder.");
}
} catch (final ClassNotFoundException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
public static List<Class<? extends SchemaBuilder>> getBuilders() {
final List<Class<? extends SchemaBuilder>> retval = new ArrayList<Class<? extends SchemaBuilder>>();
final ClassLoaders loaders = ClassLoaders.getAppLoaders(
SchemaBuilder.class, SchemaBuilder.class, false);
final DiscoverClasses<SchemaBuilder> dc = new DiscoverClasses<SchemaBuilder>(
loaders);
final ResourceNameIterator classIter = (new DiscoverServiceNames(
loaders)).findResourceNames(SchemaBuilder.class.getName());
final List<String> lst = new ArrayList<String>();
// we build a list first because the classes can be found by
// multiple loaders.
while (classIter.hasNext()) {
final String className = classIter.nextResourceName();
if (!lst.contains(className)) {
lst.add(className);
}
}
// now just load the classes once.
for (final String className : lst) {
final ResourceClassIterator<SchemaBuilder> iter = dc
.findResourceClasses(className);
while (iter.hasNext()) {
final Class<? extends SchemaBuilder> clazz = iter
.nextResourceClass().loadClass();
if (!retval.contains(clazz)) {
retval.add(clazz);
}
}
}
// return the list
return retval;
}
public static String getDescription(
final Class<? extends SchemaBuilder> clazz) {
return Util.getField(clazz, "DESCRIPTION", clazz.getName());
}
private static String getField(
final Class<? extends SchemaBuilder> clazz,
final String fieldName, final String defaultValue) {
try {
final Field f = clazz.getField(fieldName);
if (Modifier.isStatic(f.getModifiers())) {
return f.get(null).toString();
}
} catch (final NoSuchFieldException e) {
// do nothing -- acceptable
} catch (final SecurityException e) {
// do nothing -- acceptable
} catch (final IllegalArgumentException e) {
// do nothing -- acceptable
} catch (final IllegalAccessException e) {
// do nothing -- acceptable
}
return defaultValue;
}
public static String getName(final Class<? extends SchemaBuilder> clazz) {
return Util.getField(clazz, "BUILDER_NAME", clazz.getSimpleName());
}
}
Set<RdfTable> getTables(final RdfSchema schema);
}