tools.xor.service.AbstractDataAccessService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xor Show documentation
Show all versions of xor Show documentation
Empowering Model Driven Architecture in J2EE applications
/**
* XOR, empowering Model Driven Architecture in J2EE applications
*
* Copyright (c) 2012, Dilip Dalton
*
* 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.
*/
package tools.xor.service;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import tools.xor.AbstractProperty;
import tools.xor.EntityType;
import tools.xor.ExtendedProperty;
import tools.xor.OpenType;
import tools.xor.Property;
import tools.xor.Settings;
import tools.xor.Type;
import tools.xor.TypeMapper;
import tools.xor.TypeNarrower;
import tools.xor.generator.Generator;
import tools.xor.generator.LinkedChoices;
import tools.xor.generator.Lot;
import tools.xor.generator.RandomSubset;
import tools.xor.service.exim.ExcelExportImport;
import tools.xor.util.ApplicationConfiguration;
import tools.xor.util.ClassUtil;
import tools.xor.util.Constants;
import tools.xor.util.DFAtoNFA;
import tools.xor.util.Edge;
import tools.xor.util.State;
import tools.xor.util.graph.StateGraph;
import tools.xor.view.AggregateView;
import tools.xor.view.QueryBuilder;
import tools.xor.view.View;
public abstract class AbstractDataAccessService implements DataAccessService {
private static final Logger logger = LogManager.getLogger(new Exception().getStackTrace()[0].getClassName());
protected TypeMapper typeMapper;
protected DASFactory dasFactory;
protected static final String DEFAULT_SHAPE = "_DEFAULT_";
protected Map shapes; // Contains all the initialized shapes
private ThreadLocal overriddenShape = new ThreadLocal(); // temporarily overridden by user
public AbstractDataAccessService(DASFactory factory) {
this.dasFactory = factory;
this.shapes = new HashMap<>();
shapes.put(DEFAULT_SHAPE, new Shape(DEFAULT_SHAPE, null, this));
}
@Override
public Shape getShape() {
// Needs to be always present to allow user overrides
if(hasOverriddenShape()) {
return getOverriddenShape();
}
return shapes.get(DEFAULT_SHAPE);
}
@Override
public Shape getOwner(EntityType entityType) {
Shape result = getShape();
Shape parent = null;
do {
parent = result.getParent();
if(result.hasType(entityType)) {
return result;
}
result = parent;
}while (result.getShapeStrategy() == Shape.ShapeStrategy.SHARED && parent != null);
return null;
}
protected boolean hasOverriddenShape() {
return overriddenShape.get() != null;
}
protected Shape getOverriddenShape() {
return overriddenShape.get();
}
/**
* Override the current shape
* @param name for the overridden shape
* @param reuse true if reuse shape with same name
* @return the new shape that overrides the existing shape
*/
public Shape overrideShape(String name, boolean reuse) {
if(shapes.containsKey(name) && !reuse) {
throw new RuntimeException("A Shape object already exists with the name: " + name);
}
Shape result = getOrCreateShape(name, getShape());
overriddenShape.set(result);
return result;
}
protected void removeShapeOverride(boolean delete) {
if(delete) {
shapes.remove(overriddenShape.get().getName());
}
overriddenShape.remove();
}
@Override
public Shape getOrCreateShape (String name, Shape parent) {
Shape shape = shapes.get(name);
if(shape == null) {
shape = new Shape(name, shapes.get(DEFAULT_SHAPE), this);
shapes.put(name, shape);
}
return shape;
}
protected Shape getOrCreateShape (String name) {
return getOrCreateShape(name, shapes.get(DEFAULT_SHAPE));
}
@Override
public void addOpenType(OpenType type) {
getShape().addOpenType(type);
}
@Override
public Type getType(Class> clazz) {
return getShape().getType(clazz);
}
/**
* This is a performance intensive method. So we now define subtypes as and when they are
* needed.
* For this to work we save the DataAccessService instance along with the EntityType.
*
* @param shape of the type being defined
*/
protected void defineSuperType (Shape shape){
List entityTypes = new ArrayList();
for(Type type: shape.getUniqueTypes()) {
if(!EntityType.class.isAssignableFrom(type.getClass()))
continue;
EntityType extendedType = (EntityType) type;
entityTypes.add(extendedType);
}
for(EntityType type: entityTypes) {
// Initialize supertype if applicable
Class> clazz = type.getInstanceClass();
while(clazz != Object.class) {
Type superType = shape.getType(clazz.getSuperclass().getName());
if(superType != null) {
type.setSuperType((EntityType) superType);
break;
}
clazz = clazz.getSuperclass();
}
}
}
@Override
public Type getExternalType(Class> clazz) {
return getShape().getExternalType(clazz);
}
@Override
public Type getType(String name) {
return getShape().getType(name);
}
@Override
public Type getExternalType(String name) {
return getShape().getExternalType(name);
}
@Override
public TypeMapper getTypeMapper() {
return typeMapper;
}
@Override
public List getTypes() {
return new ArrayList(getShape().getUniqueTypes());
}
@Override
public void postProcess(Object newInstance, boolean autoWire) {
if(newInstance == null)
return;
if(autoWire) {
// Since we are manually creating the instance we need to autowire any dependencies
dasFactory.injectDependencies(newInstance, null);
}
}
protected void postProcess(Shape shape) {
initPositionProperty(shape);
initExternal(shape);
initRootType(shape);
// initViews(shape);
initOrder(shape);
initEnd(shape);
}
/**
* Should be invoked as the final step of the Shape object construction.
* @param shape object being constructed
*/
protected void initEnd(Shape shape) {
shape.initEnd();
shape.setBuildFinished(true);
}
protected void initOrder(Shape shape) {
// State graph of all the entity types in topological order
// Entity state graph is most likely a forest, so there is no root state
StateGraph> stateGraph = new StateGraph>(null, shape);
for(Type type: shape.getUniqueTypes()) {
if(EntityType.class.isAssignableFrom(type.getClass())) {
stateGraph.addVertex(new State(type, false));
}
}
stateGraph.populateEdges(shape);
DFAtoNFA.processInheritance(stateGraph, false);
if (!ApplicationConfiguration.config().containsKey(Constants.Config.TOPO_SKIP)
|| !ApplicationConfiguration.config().getBoolean(Constants.Config.TOPO_SKIP)) {
try {
stateGraph.toposort(shape);
}
catch (RuntimeException re) {
throw re;
}
stateGraph.orderTypes();
// Print out the graph if so configured
if (ApplicationConfiguration.config().containsKey(Constants.Config.TOPO_VISUAL)
&& ApplicationConfiguration.config().getBoolean(Constants.Config.TOPO_VISUAL)) {
Settings settings = new Settings();
settings.setGraphFileName("ApplicationStateGraph" + shape.getName() + ".dot");
stateGraph.generateVisual(settings);
stateGraph.printEntityOrder();
}
}
}
@Override
public List getViewNames() {
return getShape().getViewNames();
}
@Override
public View getView(String viewName) {
return getShape().getView(viewName);
}
@Override
public List getViews() {
return getShape().getViews();
}
@Override
public View getView(EntityType type) {
return getShape().getView(type);
}
@Override
public void addView(AggregateView view) {
getShape().addView(view);
}
@Override
public View getBaseView(EntityType type) {
return getShape().getBaseView(type);
}
@Override
public void sync(AggregateManager am, Map> avVersions) {
for(Shape shape: shapes.values()) {
shape.sync(am, avVersions);
}
}
@Override
public void refresh(TypeNarrower typeNarrower) {
getShape().refresh(typeNarrower);
}
@Override
public Class> getNarrowedClass(Class> entityClass, String viewName) {
return getShape().getNarrowedClass(entityClass, viewName);
}
@Override
public void populateNarrowedClass(Class> superClass, TypeNarrower typeNarrower) {
getShape().populateNarrowedClass(superClass, typeNarrower);
}
/**
* This can be a performance issue depending on how many entities there are in the system
*/
private void initViews(Shape shape) {
for(Type type: shape.getUniqueTypes()) {
if(EntityType.class.isAssignableFrom(type.getClass())) {
getView((EntityType) type);
}
}
}
private void initRootType(Shape shape) {
shape.initRootType();
}
private void initPositionProperty(Shape shape) {
shape.initPositionProperty();
}
protected void initExternal (Shape shape) {
shape.deriveExternal();
}
@Override
public void addProperty (Property openProperty) {
getShape().addProperty(openProperty);
}
@Override
public void removeProperty (Property openProperty) {
getShape().removeProperty(openProperty);
}
@Override
public void addOpenProperty (Property openProperty) {
getShape().addOpenProperty(openProperty);
}
@Override
public void removeOpenProperty (Property openProperty) {
getShape().removeOpenProperty(openProperty);
}
public QueryBuilder getQueryBuilder() {
return new QueryBuilder();
}
public void initGenerators(InputStream is) {
try {
Workbook wb = WorkbookFactory.create(is);
Sheet domainSheet = wb.getSheet(Constants.XOR.DOMAIN_TYPE_SHEET);
if (domainSheet == null) {
throw new RuntimeException("The Domain types sheet is missing");
}
for (int i = 1; i <= domainSheet.getLastRowNum(); i++) {
Row row = domainSheet.getRow(i);
String entityTypeName = row.getCell(1).getStringCellValue();
String sheetName = row.getCell(0).getStringCellValue();
String incomingProperty = row.getCell(2) == null ? null :
row.getCell(2).getStringCellValue();
EntityType entityType = (EntityType)getType(entityTypeName);
Sheet entitySheet = wb.getSheet(sheetName);
processDomainValues(entityType, entitySheet, incomingProperty);
}
} catch (Exception e) {
throw ClassUtil.wrapRun(e);
}
}
private void processDomainValues(EntityType entityType, Sheet entitySheet, String incomingProperty) throws
ClassNotFoundException,
NoSuchMethodException,
IllegalAccessException,
InvocationTargetException,
InstantiationException
{
Map headerMap = ExcelExportImport.getHeaderMap(entitySheet);
Lot lot = null;
for(Map.Entry entry: headerMap.entrySet()) {
Row row = entitySheet.getRow(1);
Class generatorClass = Class.forName(row.getCell(entry.getValue()).getStringCellValue());
List list = new ArrayList();
for(int i = 2; i <= entitySheet.getLastRowNum(); i++) {
row = entitySheet.getRow(i);
Cell cell = row.getCell(entry.getValue());
String value = null;
if (cell != null) {
try {
if (cell.getStringCellValue() != null) {
value = cell.getStringCellValue();
}
}
catch (Exception e) {
value = Double.toString(cell.getNumericCellValue());
}
}
if(value == null) {
break;
}
list.add(value);
}
String[] values = list.toArray(new String[list.size()]);
Constructor cd = generatorClass.getConstructor(String[].class);
Generator gen = (Generator)cd.newInstance((Object)values);
if(gen instanceof LinkedChoices) {
if(lot == null) {
lot = new Lot(((LinkedChoices)gen).getValues().length);
}
((LinkedChoices)gen).setLot(lot);
}
if(gen instanceof RandomSubset) {
gen.init(new StateGraph.ObjectGenerationVisitor(null, null, null));
}
ExtendedProperty property = (ExtendedProperty)entityType.getProperty(entry.getKey());
if(incomingProperty == null || "".equals(incomingProperty.trim())) {
incomingProperty = AbstractProperty.TYPE_GENERATOR;
}
// It can happen that that particular property is not present in
// the entity type's shape
if(property != null) {
property.setGenerator(incomingProperty, gen);
// Have it apply to all subtypes also
for (EntityType subType : entityType.getSubtypes()) {
property = (ExtendedProperty)subType.getProperty(entry.getKey());
property.setGenerator(incomingProperty, gen);
}
}
}
}
public Settings.SettingsBuilder settings() {
Settings.SettingsBuilder result = new Settings.SettingsBuilder(getShape());
return result;
}
}