org.xerial.lens.ObjectMapper Maven / Gradle / Ivy
/*--------------------------------------------------------------------------
* Copyright 2009 Taro L. Saito
*
* Licensed 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.
*--------------------------------------------------------------------------*/
//--------------------------------------
// XerialJ
//
// ObjectMapper.java
// Since: May 19, 2009 1:29:23 PM
//
// $URL$
// $Author$
//--------------------------------------
package org.xerial.lens;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.xerial.core.XerialError;
import org.xerial.core.XerialErrorCode;
import org.xerial.core.XerialException;
import org.xerial.lens.impl.MapEntry;
import org.xerial.lens.impl.ParameterSetter;
import org.xerial.lens.impl.RelationSetter;
import org.xerial.lens.impl.ParameterSetter.MapEntryBinder;
import org.xerial.lens.relation.FD;
import org.xerial.lens.relation.Node;
import org.xerial.lens.relation.query.AmoebaJoinHandler;
import org.xerial.lens.relation.query.AmoebaJoinHandlerBase;
import org.xerial.lens.relation.query.QuerySet;
import org.xerial.lens.relation.query.StreamAmoebaJoin;
import org.xerial.lens.relation.query.QuerySet.QuerySetBuilder;
import org.xerial.lens.relation.schema.Schema;
import org.xerial.lens.relation.schema.SchemaBuilder;
import org.xerial.lens.tree.TreeParser;
import org.xerial.util.ArrayDeque;
import org.xerial.util.Deque;
import org.xerial.util.bean.TypeConverter;
import org.xerial.util.bean.TypeInfo;
import org.xerial.util.log.Logger;
/**
* Object-Tree mapping processor
*
* @author leo
*
*/
public class ObjectMapper {
private static Logger _logger = Logger.getLogger(ObjectMapper.class);
// schema -> binder
private final HashMap schema2binder = new HashMap();
final QuerySet qs;
//final ObjectHandler> handler;
private static HashMap, ObjectMapper> prebuiltMapper = new HashMap, ObjectMapper>();
public ObjectMapper(Class targetType) throws XerialException {
qs = buildQuery(targetType);
}
public ObjectMapper(Class targetType, String targetNodeName) throws XerialException {
qs = buildFindQuery(targetType, targetNodeName);
}
public static ObjectMapper getMapper(Class< ? > targetType) throws XerialException {
if (prebuiltMapper.containsKey(targetType))
return prebuiltMapper.get(targetType);
else {
ObjectMapper newInstance = new ObjectMapper(targetType);
prebuiltMapper.put(targetType, newInstance);
return newInstance;
}
}
public T map(Class targetType, TreeParser parser) throws XerialException {
T object = TypeInfo.createInstance(targetType);
return map(object, parser);
}
public T map(T object, TreeParser parser) throws XerialException {
MappingProcess mp = new MappingProcess();
return mp.execute(qs, object, parser);
}
public void find(ObjectHandler handler, String targetNodeName, TreeParser parser)
throws XerialException {
MappingProcess mp = new MappingProcess();
mp.handler = handler;
mp.execute(qs, "root", parser); // "root" is a dummy object
}
private QuerySet buildQuery(Class< ? > targetType) {
QueryBuilder qb = new QueryBuilder();
qb.build(targetType, "root");
return qb.queryBuilder.build();
}
private QuerySet buildFindQuery(Class< ? > targetType, String targetNodeName) {
QueryBuilder qb = new QueryBuilder();
qb.buildFindQuery(targetType, targetNodeName);
return qb.queryBuilder.build();
}
/**
* Build query components (schemas of two nodes) from the object lens
*
* @author leo
*
*/
private class QueryBuilder {
QuerySetBuilder queryBuilder = new QuerySetBuilder();
Schema rootAndTargetNodeSchema = null;
private final HashMap>> processedClassTable = new HashMap>>();
//Set> processedClasses = new HashSet>();
public QueryBuilder() {
}
public void buildFindQuery(Class< ? > targetType, String targetNodeName) {
SchemaBuilder b = new SchemaBuilder();
b.add("root");
b.add(targetNodeName, FD.ZERO_OR_MORE);
rootAndTargetNodeSchema = b.build();
queryBuilder.addQueryTarget(rootAndTargetNodeSchema);
build(targetType, targetNodeName);
schema2binder.put(rootAndTargetNodeSchema, new TargetNodeReporter(targetType));
}
public void build(Class< ? > targetType, String alias) {
// TODO context-based schema should be used.
// e.g. (book, name) and (person, name) share the same 'name' node
if (TypeInfo.isBasicType(targetType) || targetType == MapEntry.class)
return;
Set> processed = processedClassTable.get(alias);
if (processed == null) {
processed = new HashSet>();
processedClassTable.put(alias, processed);
}
if (processed.contains(targetType))
return;
else
processed.add(targetType);
ObjectLens lens = ObjectLens.getObjectLens(targetType);
if (_logger.isTraceEnabled())
_logger.trace(String.format("class %s: %s\n", targetType.getSimpleName(), lens));
for (ParameterSetter each : lens.getSetterList()) {
if (each.getClass() == MapEntryBinder.class) {
SchemaBuilder builder = new SchemaBuilder();
builder.add("entry");
builder.add(each.getParameterName());
Schema s = builder.build();
queryBuilder.addQueryTarget(s);
schema2binder.put(s, new AttributeBinder(MapEntry.class, each));
continue;
}
if (TypeInfo.isMap(each.getParameterType())) {
// (param, *)
SchemaBuilder builder = new SchemaBuilder();
builder.add(each.getParameterName());
builder.add("*");
Schema s = builder.build();
queryBuilder.addQueryTarget(s);
schema2binder.put(s, new PropertyBinder(ObjectLens.getObjectLens(each
.getParameterType())));
// (alias, param)
Schema s2 = new SchemaBuilder().add(alias).add(each.getParameterName()).build();
queryBuilder.addQueryTarget(s2);
schema2binder.put(s2, new AttributeBinder(lens.getTargetType(), each));
continue;
}
build(each.getParameterType(), each.getParameterName());
SchemaBuilder builder = new SchemaBuilder();
builder.add(alias);
builder.add(each.getParameterName());
Schema s = builder.build();
queryBuilder.addQueryTarget(s);
schema2binder.put(s, new AttributeBinder(lens.getTargetType(), each));
}
for (RelationSetter each : lens.getRelationSetterList()) {
build(each.getCoreNodeType(), each.getCoreNodeName());
build(each.getAttributeNodeType(), each.getAttributeNodeName());
Schema s = new SchemaBuilder().add(each.getCoreNodeName()).add(
each.getAttributeNodeName()).build();
queryBuilder.addQueryTarget(s);
schema2binder.put(s, new RelationBinder(lens, each));
}
if (lens.hasPropertySetter()) {
// binding rule for the property setter
SchemaBuilder builder = new SchemaBuilder();
builder.add(alias);
builder.add("*");
// (alias, *)
Schema s = builder.build();
queryBuilder.addQueryTarget(s);
schema2binder.put(s, new PropertyBinder(lens));
}
}
}
/**
* interface for invoking setters or field setters of the object
*
* @author leo
*
*/
private static interface Binder {
public void bind(MappingProcess proc, Schema schema, Node coreNode, Node attributeNode)
throws XerialException;
public void bindText(MappingProcess proc, Schema schema, Node coreNode, Node textNode,
String textValue) throws XerialException;
}
private static class TargetNodeReporter implements Binder {
private final Class targetNodeType;
@SuppressWarnings("unchecked")
public TargetNodeReporter(Class< ? > targetNodeType) {
this.targetNodeType = (Class) targetNodeType;
}
@SuppressWarnings("unchecked")
public void bind(MappingProcess proc, Schema schema, Node rootNode, Node targetNode)
throws XerialException {
Object targetNodeInstance = proc.getNodeInstance(targetNode, targetNodeType);
try {
((ObjectHandler) proc.getObjectHandler()).handle(targetNodeType
.cast(targetNodeInstance));
}
catch (Exception e) {
throw XerialException.convert(e);
}
}
public void bindText(MappingProcess proc, Schema schema, Node coreNode, Node textNode,
String textValue) throws XerialException {
// ignore the text event
}
}
/**
* Binds a node pair to the context node object. e.g. addA_B(A a, B b)
*
* @author leo
*
*/
private static class RelationBinder implements Binder {
private final ObjectLens lens;
private final RelationSetter setter;
public RelationBinder(ObjectLens lens, RelationSetter setter) {
this.lens = lens;
this.setter = setter;
}
public void bind(MappingProcess proc, Schema schema, Node coreNode, Node attributeNode)
throws XerialException {
Object coreNodeInstance = proc.getNodeInstance(coreNode, setter.getCoreNodeType());
Object attributeNodeInstance = proc.getNodeInstance(attributeNode, setter
.getAttributeNodeType());
Object contextNode = findContextNode(proc, lens.getTargetType());
if (contextNode == null)
throw new XerialException(XerialErrorCode.INVALID_INPUT, "no context node for "
+ setter);
if (attributeNodeInstance != null) {
setter.bind(contextNode, coreNodeInstance, attributeNodeInstance);
}
}
public Object findContextNode(MappingProcess proc, Class< ? > targetType) {
for (Iterator
© 2015 - 2024 Weber Informatics LLC | Privacy Policy