com.quinsoft.zeidon.objectdefinition.LodDef Maven / Gradle / Ivy
The newest version!
/**
Zeidon JOE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zeidon JOE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Zeidon JOE. If not, see .
Copyright 2009-2015 QuinSoft
*/
package com.quinsoft.zeidon.objectdefinition;
import com.quinsoft.zeidon.Application;
import com.quinsoft.zeidon.CacheMap;
import com.quinsoft.zeidon.ObjectConstraintException;
import com.quinsoft.zeidon.ObjectConstraintType;
import com.quinsoft.zeidon.ObjectEngine;
import com.quinsoft.zeidon.Task;
import com.quinsoft.zeidon.TaskQualification;
import com.quinsoft.zeidon.UnknownEntityDefException;
import com.quinsoft.zeidon.UnknownLodDefException;
import com.quinsoft.zeidon.View;
import com.quinsoft.zeidon.ZeidonException;
import com.quinsoft.zeidon.config.ZeidonPreferences;
import com.quinsoft.zeidon.dbhandler.DbHandler;
import com.quinsoft.zeidon.domains.Domain;
import com.quinsoft.zeidon.utils.CacheMapImpl;
import com.quinsoft.zeidon.utils.JoeUtils;
import com.quinsoft.zeidon.utils.PortableFileReader;
import com.quinsoft.zeidon.utils.PortableFileReader.PortableFileAttributeHandler;
import com.quinsoft.zeidon.utils.PortableFileReader.PortableFileEntityHandler.NullEntityHandler;
import org.apache.commons.lang3.StringUtils;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author DG
*
*/
public class LodDef implements PortableFileAttributeHandler
{
private final Application app;
private String name;
private final String filename;
private final Map nameMap = new HashMap();
private List entityList = new ArrayList();
private String erDate;
private boolean hasGenKey;
private String genkeyHandler;
private String database;
private boolean activateConstraint;
private boolean commitConstraint;
private String constraintOper;
private int height = 0;
private LockingLevel lockingLevel = LockingLevel.NONE;
private SourceFileType sourceFileType = SourceFileType.VML;
private String sourceFileName;
private boolean hasLazyLoadEntities;
private boolean hasDuplicateInstances;
/**
* True if any entities in this LOD have DataRecords.
*/
private boolean hasPhysicalMappings = false;
private String libraryName;
private CacheMap cacheMap;
/**
* If true, then by default we'll load entities with a single select.
*/
boolean singleSelectDefault;
static private final Class>[] constructorArgTypes = new Class>[] { View.class };
public LodDef(Task task, Application app, String name) throws UnknownLodDefException
{
super();
this.app = app;
this.name = name;
// Make sure the XOD exists.
String xod = name + ".XOD";
// We know that system apps XODs are stored as all lower-case.
if ( app.isSystemApp() )
xod = xod.toLowerCase();
filename = app.getObjectDir() + "/" + xod;
InputStream inputStream = JoeUtils.getInputStream( task, filename, getClass().getClassLoader() );
if ( inputStream == null )
throw new UnknownLodDefException( name, filename, app );
ZeidonPreferences prefs = app
.getObjectEngine()
.getZeidonPreferences( app );
String singleSelectConfig = prefs.get( app.getName(), "ActivateWithSingleSelect", "N" );
singleSelectDefault = "YyTt1".contains( singleSelectConfig.substring( 0, 1 ) );
loadLodDef( task, inputStream );
}
public Application getApplication()
{
return app;
}
public String getName()
{
return name;
}
/**
* Returns the full filename of the XOD that defined this OD.
* @return XOD file name
*/
public String getFileName()
{
return filename;
}
public EntityDef getEntityDef( String entityName, boolean required, boolean ignoreCase )
{
// We allow the dbhandler to use a special string to indicate the
// root.
if ( StringUtils.equals( entityName, DbHandler.ROOT_ENTITY ) )
return getEntityDef( 0 ); // Return the root LodDef.
String searchName = entityName;
if ( ignoreCase )
searchName = searchName.toLowerCase();
EntityDef entityDef = nameMap.get( searchName );
if ( entityDef == null && required )
throw new UnknownEntityDefException( this, entityName );
return entityDef;
}
public EntityDef getEntityDef( String entityName, boolean required )
{
return getEntityDef( entityName, required, false );
}
public EntityDef getEntityDef( String entityName )
{
return getEntityDef( entityName, true, false );
}
public EntityDef getEntityDef( int index )
{
EntityDef entityDef = entityList.get( index );
return entityDef;
}
private void loadLodDef( Task task, InputStream file )
{
try
{
PortableFileReader.readPortableFile( task.getObjectEngine(), file, task.log(), new LodDefHandler( this ) );
task.log().info( "LodDef %s loaded from: %s", this, filename );
}
catch ( Exception e )
{
throw ZeidonException.wrapException( e ).prependFilename( filename );
}
}
private void addEntityDef( EntityDef entityDef )
{
nameMap.put( entityDef.getName(), entityDef );
nameMap.put( entityDef.getName().toLowerCase(), entityDef );
height = Math.max( height, entityDef.getDepth() );
entityList.add( entityDef );
}
@Override
public void setAttribute(PortableFileReader reader)
{
switch ( reader.getAttributeName() )
{
case "NAME":
if ( ! reader.getAttributeValue().equalsIgnoreCase( name ) )
throw new ZeidonException("Name of object from XOD ('%s') doesn't match OD name", reader.getAttributeValue());
// Override the name used when this object was being created with the one from the .xod file.
// We need to do this because names are case-insensitive and the user may have used a different
// case than the one from the file.
name = reader.getAttributeValue().intern();
break;
case "ER_DATE":
erDate = reader.getAttributeValue().intern();
break;
case "DATABASE":
case "DFT_DBNANME":
database = reader.getAttributeValue().intern();
break;
case "DBH_Data":
// We don't actually use DBH_Data in JOE but this code is here because we need to
// read past the blob data.
int lth = Integer.parseInt( reader.getAttributeValue() );
byte[] buffer = new byte[ lth ];
try
{
reader.getStreamReader().read( buffer, lth );
}
catch ( Throwable e )
{
throw ZeidonException.wrapException( e );
}
break;
case "GKHANDLER":
genkeyHandler = reader.getAttributeValue().intern();
break;
case "LOCK":
Integer level = Integer.valueOf( reader.getAttributeValue() );
lockingLevel = LockingLevel.lookup( level );
break;
case "OCEOPER":
constraintOper = reader.getAttributeValue().intern();
break;
case "OCACT":
activateConstraint = reader.getAttributeValue().startsWith( "Y" );
break;
case "OCCOM":
commitConstraint = reader.getAttributeValue().startsWith( "Y" );
break;
case "OCSRCFILE":
sourceFileName = reader.getAttributeValue();
if ( ! sourceFileName.contains( "." ) )
sourceFileName = getApplication().getPackage() + "." + sourceFileName;
break;
case "OCSRCTYPE":
sourceFileType = SourceFileType.parse( reader.getAttributeValue() );
break;
case "OPER_LIBNM":
libraryName = reader.getAttributeValue().intern();
break;
}
}
/**
* Returns the list of EntityDefs in hierarchical order.
*
* @return
*/
public List getEntityDefs()
{
return entityList;
}
public int getEntityCount()
{
return entityList.size();
}
@Override
public String toString()
{
return getApplication().toString() + "." + name;
}
public String getErDate()
{
return erDate;
}
public String getDatabase()
{
return database;
}
public EntityDef getRoot()
{
return entityList.get( 0 );
}
/**
* Traces the LodDef to the log
* @param task
*/
public void displayLodDef( TaskQualification task )
{
task.log().info( "Displaying LodDef for %s", getName() );
for ( EntityDef entityDef : getEntityDefs() )
{
task.log().info( "%s %d, count = %d", entityDef.getName(),
entityDef.getDepth(), entityDef.getChildCount() );
for ( AttributeDef attrib : entityDef.getAttributes() )
{
Domain domain = attrib.getDomain();
String type = domain == null ? attrib.getType().toString() : domain.getName();
task.log().info( " Attrib: %s %d Domain:%s",
attrib.getName(), attrib.getAttributeNumber(), type );
}
DataRecord dataRecord = entityDef.getDataRecord();
if ( dataRecord == null )
continue;
task.log().info( " DataRecord: %s type=%s", dataRecord.getRecordName(), dataRecord.getType() );
RelRecord relRecord = dataRecord.getRelRecord();
if ( relRecord == null )
continue;
task.log().info( " RelRecord: %s type=%s", relRecord.getRecordName(), relRecord.getRelationshipType() );
for ( RelField relField : relRecord.getRelFields() )
{
task.log().info( " RelField: %s, %d, %d", relField.getFieldName(),
relField.getRelToken(), relField.getSrcToken() );
}
}
}
void setHasGenKey(boolean hasGenKey)
{
this.hasGenKey = hasGenKey;
}
public boolean hasGenKey()
{
return hasGenKey;
}
public boolean hasActivateConstraint()
{
return activateConstraint;
}
public boolean hasCommitConstraint()
{
return commitConstraint;
}
private Object getConstraintObject( View view )
{
ObjectEngine oe = view.getObjectEngine();
String className = getSourceFileName();
if ( StringUtils.isBlank( className ) )
className = getApplication().getPackage() + "." + getName() + "_Object";
try
{
ClassLoader classLoader = oe.getClassLoader( className );
Class> operationsClass;
operationsClass = classLoader.loadClass( className );
Constructor> constructor = operationsClass.getConstructor( constructorArgTypes );
return constructor.newInstance( view );
}
catch ( Exception e )
{
throw ZeidonException.wrapException( e )
.prependLodDef( LodDef.this )
.appendMessage( "Class name = %s", className )
.appendMessage( "See inner exception for more info." );
}
}
static private final Class>[] ARGUMENT_TYPES1 = new Class>[] { View.class, Integer.class, Integer.class };
static private final Class>[] ARGUMENT_TYPES2 = new Class>[] { View.class, ObjectConstraintType.class };
private int executeConstraint( View view, ObjectConstraintType type )
{
switch ( sourceFileType )
{
case VML:
return executeVmlConstraint( view, type );
case SCALA:
return executeScalaConstraint( view, type );
case JAVA:
default:
throw new ZeidonException( "Unsupported Entity Constraint SourceFileType: %s", type );
}
}
private int executeVmlConstraint( View view, ObjectConstraintType type )
{
Object object = getConstraintObject( view );
try
{
Method method;
try
{
method = object.getClass().getMethod( getConstraintOper(), ARGUMENT_TYPES1 );
Integer rc = (Integer) method.invoke( object, view, type.getVmlValue(), 0 );
return rc;
}
catch ( NoSuchMethodException e1 )
{
method = object.getClass().getMethod( getConstraintOper(), ARGUMENT_TYPES2 );
Integer rc = (Integer) method.invoke( object, view, type );
return rc;
}
}
catch ( InvocationTargetException e )
{
Throwable target = e.getTargetException();
if ( target instanceof ZeidonException )
throw (ZeidonException) target;
target = e.getCause();
if ( target instanceof ZeidonException )
throw (ZeidonException) target;
throw ZeidonException.wrapException( e )
.appendMessage( "Class = %s", object.getClass().getCanonicalName() )
.appendMessage( "Method name = %s", getConstraintOper() );
}
catch ( Exception e )
{
throw ZeidonException.wrapException( e )
.appendMessage( "Class = %s", object.getClass().getCanonicalName() )
.appendMessage( "Method name = %s", getConstraintOper() );
}
}
private int executeScalaConstraint( View view, ObjectConstraintType type )
{
try
{
return view.getTask().getScalaHelper().executeObjectConstraint( view, type );
}
catch ( Exception e )
{
if ( e instanceof InvocationTargetException )
throw ZeidonException.wrapException( ((InvocationTargetException) e).getTargetException() );
else
throw ZeidonException.wrapException( e );
}
}
/**
* Executes the activate constraint on the view.
*
* @param view
* @return
*/
public int executeActivateConstraint( View view ) throws ObjectConstraintException
{
if ( ! hasActivateConstraint() )
return 0;
try
{
return executeConstraint( view, ObjectConstraintType.ACTIVATE );
}
catch ( Exception e )
{
if ( e instanceof InvocationTargetException )
throw ZeidonException.wrapException( ((InvocationTargetException) e).getTargetException() );
else
throw ZeidonException.wrapException( e );
}
}
/**
* Executes the commit constraint on the view.
*
* @param view
* @return
*/
public int executeCommitConstraint( View view ) throws ObjectConstraintException
{
if ( ! hasCommitConstraint() )
return 0;
try
{
return executeConstraint( view, ObjectConstraintType.COMMIT );
}
catch ( Exception e )
{
throw ZeidonException.prependMessage( e, "Error calling Commit constraint for %s", this );
}
}
public String getConstraintOper()
{
return constraintOper;
}
private class LodDefHandler extends NullEntityHandler
{
private final LodDef lodDef;
private EntityDef currentEntityDef = null;
private final ArrayList parentStack = new ArrayList();
LodDefHandler(LodDef lodDef)
{
super();
this.lodDef = lodDef;
parentStack.add( null );
}
@Override
public PortableFileAttributeHandler createEntity( PortableFileReader reader, int level, long flags )
{
switch ( reader.getAttributeName() )
{
case "ATTRIB":
{
AttributeDef attrib = new AttributeDef(currentEntityDef);
return attrib;
}
case "CHILDENTITY":
case "ENTITY":
{
// Subtract one from level to take into account that the level 1 entity
// is the object name.
level--;
if ( currentEntityDef != null )
addEntityDef( currentEntityDef );
EntityDef entityDef = new EntityDef( lodDef, level );
if ( level >= parentStack.size() )
parentStack.add( entityDef );
else
parentStack.set( level, entityDef );
if ( level > 1 )
entityDef.setParent( parentStack.get( level - 1 ) );
if ( currentEntityDef != null )
currentEntityDef.setNextHier( entityDef );
entityDef.setPrevHier( currentEntityDef );
currentEntityDef = entityDef;
return entityDef;
}
case "DATAFIELD":
{
DataField dataField = new DataField();
currentEntityDef.getDataRecord().addDataField( dataField );
return dataField;
}
case "RELFIELD":
{
RelField relField = new RelField();
return relField;
}
case "RELRECORD":
{
RelRecord relRecord = new RelRecord( currentEntityDef.getDataRecord() );
currentEntityDef.getDataRecord().setRelRecord( relRecord );
return relRecord;
}
case "DATARECORD":
{
DataRecord dataRecord = new DataRecord( currentEntityDef );
currentEntityDef.setDataRecord( dataRecord );
return dataRecord;
}
case "OBJECT":
{
return lodDef;
}
default:
throw new ZeidonException( "Unknown entity '%s'", reader.getAttributeValue());
}
}
@Override
public void endEntity(PortableFileReader reader, PortableFileAttributeHandler handler, int level)
{
if ( handler instanceof EntityDef )
{
EntityDef entityDef = (EntityDef) handler;
// Count up the number of persistent and work attributes.
int persistentCount = 0;
int workCount = 0;
for ( AttributeDef attributeDef : entityDef.getAttributes() )
{
if ( attributeDef.isPersistent() )
persistentCount++;
else
workCount++;
}
entityDef.setPersistentAttributeCount( persistentCount );
entityDef.setWorkAttributeCount( workCount );
}
else
if ( handler instanceof AttributeDef )
{
AttributeDef attrib = (AttributeDef) handler;
attrib.finishAttributeLoading();
currentEntityDef.addAttributeDef( attrib );
}
else
if ( handler instanceof RelField )
{
RelField relField = (RelField) handler;
currentEntityDef.getDataRecord().getRelRecord().addRelField( relField );
}
}
@Override
public void endFile()
{
addEntityDef( currentEntityDef ); // Add the last EntityDef.
// Set the sibling pointers for all the view entities.
entityList.get( 0 ).setSiblingsForChildren();
entityList = Collections.unmodifiableList( entityList );
Map attribMap = new HashMap();
// Find the AttributeDef for each of the DataFields.
for ( EntityDef entityDef : entityList )
{
for ( AttributeDef AttributeDef : entityDef.getAttributes() )
attribMap.put( AttributeDef.getXvaAttrToken(), AttributeDef );
DataRecord dataRecord = entityDef.getDataRecord();
if ( dataRecord != null )
{
for ( DataField dataField : dataRecord.dataFields() )
{
AttributeDef AttributeDef = attribMap.get( dataField.getToken() );
assert AttributeDef != null : "Can't find matching XVA Token for DataField";
dataField.setAttributeDef( AttributeDef );
}
dataRecord.setFields( entityDef );
}
entityDef.validateEntityDef();
}
if ( hasDuplicateInstances() )
computeDuplicateInstances();
}
} // class LodDefHandler
/**
* This LodDef has entities flagged as duplicate instances. Find all the entityDefs
* flagged as duplicate instances, then make sure they all activate the total sum
* of attributes.
*/
private void computeDuplicateInstances()
{
Map> duplicateMap = new HashMap<>();
for ( EntityDef entityDef : entityList )
{
if ( ! entityDef.isDuplicateEntity() )
continue;
List list = duplicateMap.get( entityDef.getErEntityToken() );
if ( list == null )
{
list = new ArrayList<>();
duplicateMap.put( entityDef.getErEntityToken(), list );
}
list.add( entityDef );
}
assert duplicateMap.size() > 0;
for ( List list : duplicateMap.values() )
{
// Get the set of attributes that are to be activated by any entity.
Set activatedAttributes = new HashSet<>();
for ( EntityDef entityDef : list )
{
for ( AttributeDef attributeDef : entityDef.getAttributes() )
{
if ( attributeDef.isActivate() )
activatedAttributes.add( attributeDef.getErAttributeToken() );
}
}
// Now go through and make sure all the attributes have the activate
// flag set for all entities.
for ( EntityDef entityDef : list )
{
for ( String erAttributetoken : activatedAttributes )
entityDef.getAttributeByErToken( erAttributetoken ).setActivate( true );
}
}
}
public int getHeight()
{
return height;
}
/**
* @return the lockingLevel
*/
public LockingLevel getLockingLevel()
{
return lockingLevel;
}
public String getGenkeyHandler()
{
return genkeyHandler;
}
public SourceFileType getSourceFileType()
{
return sourceFileType;
}
public String getSourceFileName()
{
return sourceFileName;
}
/**
* True if the LOD has DataRecords.
*
* @return
*/
public boolean hasPhysicalMappings()
{
return hasPhysicalMappings;
}
void setHasPhysicalMappings( boolean hasPhysicalMappings )
{
this.hasPhysicalMappings = hasPhysicalMappings;
}
public String getLibraryName()
{
return libraryName;
}
/**
* Returns true if this LOD has entities that are lazy-loaded.
*
* @return
*/
public boolean hasLazyLoadEntities()
{
return hasLazyLoadEntities;
}
void setHasLazyLoadEntities( boolean hasLazyLoadEntities )
{
this.hasLazyLoadEntities = hasLazyLoadEntities;
}
boolean hasDuplicateInstances()
{
return hasDuplicateInstances;
}
void setHasDuplicateInstances( boolean hasDuplicateInstances )
{
this.hasDuplicateInstances = hasDuplicateInstances;
}
public synchronized CacheMap getCacheMap()
{
if ( cacheMap == null )
cacheMap = new CacheMapImpl();
return cacheMap;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy