/*
* Reference ETL Parser for Java
* Copyright (c) 2000-2009 Constantine A Plotnikov
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.sf.etl.parsers.beans;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.logging.Level;
import net.sf.etl.parsers.ObjectName;
import net.sf.etl.parsers.ParserException;
import net.sf.etl.parsers.TermParser;
import net.sf.etl.parsers.TermToken;
import net.sf.etl.parsers.utils.AbstractTreeParser;
/**
* This class provide facilities for mapping class names.
*
* @author const
*
* @param <BaseObjectType>
* this is a base type for returned objects
* @param <FeatureType>
* this is a type for feature metatype used by objects
* @param <MetaObjectType>
* this is a type for meta object type
* @param <HolderType>
* this is a holder type for collection properties
*/
public abstract class AbstractReflectionParser<BaseObjectType, FeatureType, MetaObjectType, HolderType>
extends
AbstractTreeParser<BaseObjectType, FeatureType, MetaObjectType, HolderType> {
/** a logger */
private static final java.util.logging.Logger log = java.util.logging.Logger
.getLogger(AbstractReflectionParser.class.getName());
/** A class loader that should be used to load classes */
protected final ClassLoader classLoader;
/** the active token collectors */
protected final ArrayList<TokenCollector> collectors = new ArrayList<TokenCollector>();
/** A map from namespace to java package */
protected final HashMap<String, String> namespaceMapping = new HashMap<String, String>();
/** A map from namespace to object to java class */
protected final HashMap<String, HashMap<String, Class<? extends Object>>> objectMapping = new HashMap<String, HashMap<String, Class<? extends Object>>>();
/**
* A constructor
*
* @param parser
* a term parser to use
* @param classLoader
* a class loader for the parser
*/
public AbstractReflectionParser(TermParser parser, ClassLoader classLoader) {
super(parser);
if (classLoader == null) {
classLoader = getClassLoader();
}
this.classLoader = classLoader;
}
/**
* This method tries to detect class loader that should be used by this
* instance in case when class loader is not provided by creator of the
* parser.
*
* The method checks contextClassLoader of the thread, and if is still not
* found, uses class loader of parser class. Note if the class is
* subclassed, a classloader of the subclass will be used.
*
* @return a class loader
*/
protected ClassLoader getClassLoader() {
ClassLoader classLoader = null;
try {
classLoader = Thread.currentThread().getContextClassLoader();
} catch (final Exception ex) {
if (log.isLoggable(Level.FINE)) {
log.log(Level.FINE,
"There is a security problem with loading classLoader",
ex);
}
}
if (classLoader == null) {
classLoader = getClass().getClassLoader();
}
return classLoader;
}
/**
* Add mapping from namespace to java package
*
* @param namespace
* a namespace
* @param javaPackage
* a java package
*/
public void mapNamespaceToPackage(String namespace, String javaPackage) {
namespaceMapping.put(namespace, javaPackage);
}
/**
* Map name to the class
*
* @param namespace
* object namespace
* @param name
* object name in parser
* @param beanClass
* class of java bean
*/
public void mapNameToClass(String namespace, String name,
Class<? extends Object> beanClass) {
HashMap<String, Class<? extends Object>> nameToClass = objectMapping
.get(namespace);
if (nameToClass == null) {
nameToClass = new HashMap<String, Class<? extends Object>>();
objectMapping.put(namespace, nameToClass);
}
nameToClass.put(name, beanClass);
}
/**
* Get object class
*
* @param name
* a name of class
* @return class for the object name
*/
protected Class<? extends Object> getObjectClass(ObjectName name) {
// check object name map
final HashMap<String, Class<? extends Object>> nameToObject = objectMapping
.get(name.namespace());
if (nameToObject != null) {
final Class<? extends Object> rc = nameToObject.get(name.name());
if (rc != null) {
return rc;
}
}
// check namespace map
final String packageName = namespaceMapping.get(name.namespace());
if (packageName != null) {
final String className = packageName + "." + name.name();
Class<? extends Object> rc;
try {
if (classLoader != null) {
rc = classLoader.loadClass(className);
} else {
rc = Class.forName(className);
}
return rc;
} catch (final ClassNotFoundException ex) {
if (log.isLoggable(Level.FINE)) {
log.log(Level.FINE, "Class has not been found for name: "
+ name);
}
}
}
throw new ParserException("Class not found for object name " + name);
}
/** {@inheritDoc} */
@Override
protected boolean advanceParser() {
if (!collectors.isEmpty()) {
TermToken t = parser.current();
for (TokenCollector c : collectors) {
c.collect(t);
}
}
return super.advanceParser();
}
/** {@inheritDoc} */
@Override
protected void objectEnded(BaseObjectType object) {
if (object instanceof TokenCollector) {
TokenCollector r = collectors.remove(collectors.size() - 1);
assert r == object;
}
super.objectEnded(object);
}
/** {@inheritDoc} */
@Override
protected void objectStarted(BaseObjectType object) {
if (object instanceof TokenCollector) {
collectors.add((TokenCollector) object);
}
super.objectStarted(object);
}
}