/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.transformation.materialization;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.teiid.core.designer.ModelerCoreException;
import org.teiid.core.designer.ModelerCoreRuntimeException;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.core.designer.util.StringConstants;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.metamodel.aspect.sql.SqlTableAspect;
import org.teiid.designer.core.validation.rules.StringNameValidator;
import org.teiid.designer.core.workspace.ModelResource;
import org.teiid.designer.core.workspace.ModelUtil;
import org.teiid.designer.core.workspace.ModelWorkspaceException;
import org.teiid.designer.metamodels.relational.BaseTable;
import org.teiid.designer.metamodels.relational.Column;
import org.teiid.designer.metamodels.relational.Index;
import org.teiid.designer.metamodels.relational.PrimaryKey;
import org.teiid.designer.metamodels.relational.RelationalFactory;
import org.teiid.designer.metamodels.relational.Schema;
import org.teiid.designer.metamodels.relational.Table;
import org.teiid.designer.metamodels.relational.UniqueConstraint;
import org.teiid.designer.metamodels.relational.UniqueKey;
import org.teiid.designer.transformation.TransformationPlugin;
/**
* @since 8.0
*/
public class MaterializationModelGenerator {
private static String MV_PREFIX = SqlTableAspect.MATERIALIZED_VIEW_PREFIX;
private static int STARTING_INDEX = 1000000;
private static String RESULT_MSG = TransformationPlugin.Util.getString("MVModelGenerator.resultStr"); //$NON-NLS-1$
private static String OK_MSG = TransformationPlugin.Util.getString("MVModelGenerator.success"); //$NON-NLS-1$
private static String UNDEFINED = TransformationPlugin.Util.getString("MVModelGenerator.undefined"); //$NON-NLS-1$
private static IStatus OK_STATUS = new Status(IStatus.OK, TransformationPlugin.PLUGIN_ID,
TransformationPlugin.Util.getString("MVModelGenerator.allInputsOkStatusMessage")); //$NON-NLS-1$
private final HashMap virtToPhysMappings;
private final StringNameValidator nameValidator = new StringNameValidator();
private ModelResource materializedViewModel;
private Object targetLocation;
private Collection<EObject> virtualTables;
private int nextIndex;
private MultiStatus result;
private Set usedNIS;
public MaterializationModelGenerator() {
this.virtToPhysMappings = new HashMap();
this.usedNIS = new HashSet();
nextIndex = STARTING_INDEX + 1;
}
public MultiStatus execute() {
return execute(this.materializedViewModel, this.targetLocation, this.virtualTables);
}
public MultiStatus execute(final ModelResource targetResource,
final Object targetLocation,
final Collection<EObject> virtualTables) {
CoreArgCheck.isNotNull(targetResource, "targetResource"); //$NON-NLS-1$
this.materializedViewModel = targetResource;
// System.out.println("MVModelGenerator.execute() target resource = " + targetResource.getItemName());
try {
// System.out.println("MVModelGenerator.execute() create schema for tables = " + targetResource.getItemName());
Schema schema = null;
if( targetLocation != null && targetLocation instanceof Schema) {
schema = (Schema)targetLocation;
} else {
schema = createSchema( targetResource.getPath().removeFileExtension().lastSegment() );
}
for( EObject next : virtualTables ) {
if(next instanceof Table ) {
((Table)next).setMaterialized(true);
EObject mvTable = createMaterializedView( (Table)next, schema);
((Table)next).setMaterializedTable((Table)mvTable);
}
}
} catch (Exception err) {
TransformationPlugin.Util.log(err);
}
if(result == null) {
addStatus(IStatus.OK, OK_MSG, null);
}
return result;
}
private Schema createSchema( final String inputModelName ) {
try {
for( Object eObj : materializedViewModel.getEObjects() ) {
if( eObj instanceof Schema && inputModelName.equalsIgnoreCase(ModelerCore.getModelEditor().getName((EObject)eObj)) ) {
return (Schema)eObj;
}
}
} catch (ModelWorkspaceException err) {
TransformationPlugin.Util.log(err);
}
// Create a new schema with the name of the input model
final Schema newSchema = RelationalFactory.eINSTANCE.createSchema();
newSchema.setName(inputModelName);
newSchema.setNameInSource(inputModelName);
// Add the schema to the materialized view model
addValue(this.materializedViewModel, newSchema, getModelResourceContents());
return newSchema;
}
/**
* @return Returns the virtToPhysFullNames.
* @since 4.1
*/
public HashMap getVirtToPhysMappings() {
return this.virtToPhysMappings;
}
/**
* @return Returns the resultPhysicalModel.
* @since 4.1
*/
public ModelResource getMaterializedViewModel() {
return this.materializedViewModel;
}
private EObject createMaterializedView(Table table, final Schema owner) {
//create the new table and the staging table and add them to the result physical model
final Table mv = (Table)RelationalFactory.eINSTANCE.create(table.eClass() );
//Create a uniqueName
String name = table.getName();
String tmp = nameValidator.createValidUniqueName(name);
if(tmp != null) {
name = tmp;
}
//Set table names
mv.setName(name);
//Copy table values
copyTableValues(table, mv);
// Increment only after both mat view cache and staging tables
// have been named.
++nextIndex;
owner.getTables().add(mv);
final Collection newTables = new ArrayList();
newTables.add(mv);
this.virtToPhysMappings.put(table, newTables);
//Iterate over the children of the Materialized View and create corresponding columns, primary keys, and indexes in both
//the new table and the stage table.
final Iterator cols = table.getColumns().iterator();
final HashSet indexes = new HashSet();
final HashSet pks = new HashSet();
while(cols.hasNext() ) {
final Column nextCol = (Column)cols.next();
final Column newCol = RelationalFactory.eINSTANCE.createColumn();
final Column stageCol = RelationalFactory.eINSTANCE.createColumn();
mv.getColumns().add(newCol);
copyColValues(nextCol, newCol);
copyColValues(nextCol, stageCol);
indexes.addAll(nextCol.getIndexes() );
pks.addAll(nextCol.getUniqueKeys() );
}
final Iterator pksIt = pks.iterator();
while(pksIt.hasNext() ) {
final UniqueKey nextKey = (UniqueKey)pksIt.next();
final UniqueKey newKey = (UniqueKey)RelationalFactory.eINSTANCE.create(nextKey.eClass() );
if(nextKey instanceof PrimaryKey) {
((BaseTable)mv).setPrimaryKey( (PrimaryKey)newKey);
}else {
((BaseTable)mv).getUniqueConstraints().add((UniqueConstraint) newKey);
}
copyUniqueKeyValues(nextKey, newKey);
}
final Iterator indexIt = indexes.iterator();
while(indexIt.hasNext() ) {
final Index nextIndex = (Index)indexIt.next();
final Index newIndex = RelationalFactory.eINSTANCE.createIndex();
copyIndexValues(nextIndex, newIndex, false, table, mv);
addValue(this.materializedViewModel, newIndex, getModelResourceContents());
}
return mv;
}
protected void copyTableValues(final Table orig, final Table copy) {
copy.setNameInSource( getUniqueNIS(orig) );
copy.setCardinality(orig.getCardinality() );
copy.setSystem(orig.isSystem() );
// Defect 16941 - the supportsUpdate flag should always be true for a materialization table
//copy.setSupportsUpdate(orig.isSupportsUpdate() );
copy.setSupportsUpdate(true);
}
private void copyColValues(final Column orig, final Column copy) {
copy.setAutoIncremented(orig.isAutoIncremented() );
copy.setCaseSensitive(orig.isCaseSensitive() );
copy.setCharacterSetName(orig.getCharacterSetName() );
copy.setCollationName(orig.getCollationName() );
copy.setCurrency(copy.isCurrency() );
copy.setDefaultValue(orig.getDefaultValue() );
copy.setFixedLength(orig.isFixedLength() );
copy.setFormat(orig.getFormat() );
copy.setLength(orig.getLength() );
copy.setMaximumValue(orig.getMaximumValue() );
copy.setMinimumValue(orig.getMinimumValue() );
copy.setName(orig.getName() );
copy.setNameInSource(orig.getNameInSource() );
copy.setNativeType(orig.getNativeType() );
copy.setNullable(orig.getNullable() );
copy.setPrecision(orig.getPrecision() );
copy.setRadix(orig.getRadix() );
copy.setScale(orig.getScale() );
copy.setSearchability(orig.getSearchability() );
copy.setSelectable(orig.isSelectable() );
copy.setSigned(orig.isSigned() );
copy.setType(orig.getType() );
// Defect 16941 - the updateable flag should always be true for the columns of a materialization table
//copy.setUpdateable(orig.isUpdateable() );
copy.setUpdateable(true);
}
private void addStatus(final int severity, final String msg, final Exception e) {
if(result == null) {
result = new MultiStatus(TransformationPlugin.PLUGIN_ID, -1, RESULT_MSG, null);
}
result.add(new Status(severity, TransformationPlugin.PLUGIN_ID, -1, msg, e) );
}
private void copyUniqueKeyValues(final UniqueKey orig,
final UniqueKey copy) {
copy.setName(orig.getName());
copy.setNameInSource(orig.getNameInSource());
try {
final BaseTable copyOwner = copy.getTable();
final BaseTable origOwner = orig.getTable();
final Iterator cols = orig.getColumns().iterator();
while(cols.hasNext() ) {
final Column nextOrigCol = (Column)cols.next();
final int index = origOwner.getColumns().indexOf(nextOrigCol);
final Column copyCol = (Column)copyOwner.getColumns().get(index);
if(nextOrigCol.getName().equals(copyCol.getName() ) ) {
copy.getColumns().add(copyCol);
}else {
final String msg = TransformationPlugin.Util.getString("MVModelGenerator.errorSettingUCvals", copy.getName()); //$NON-NLS-1$
throw new ModelerCoreRuntimeException(msg);
}
}
} catch (Exception err) {
final String msg = TransformationPlugin.Util.getString("MVModelGenerator.errorSettingUCvals", copy.getName()); //$NON-NLS-1$
throw new ModelerCoreRuntimeException(msg);
}
}
private void copyIndexValues(final Index orig, final Index copy, final boolean isStage, final Table origTable, final Table copyTable) {
copy.setAutoUpdate(orig.isAutoUpdate() );
copy.setFilterCondition(orig.getFilterCondition() );
copy.setName(orig.getName());
String nameInSource = orig.getNameInSource();
copy.setNameInSource(((nameInSource == null) || (nameInSource.length() == 0)) ? orig.getName() : nameInSource);
copy.setNullable(orig.isNullable() );
copy.setUnique(orig.isUnique() );
// table columns
List origTableCols = origTable.getColumns();
List copyTableCols = copyTable.getColumns();
// index columns
List origIndexCols = orig.getColumns();
List copyIndexCols = copy.getColumns(); // empty to start
// loop through original index columns. find the appropriate copy column and add to copy index.
// assume index of original table column is the same index for the same column in the copy table.
for (int size = origIndexCols.size(), i = 0; i < size; ++i) {
Column origCol = (Column)origIndexCols.get(i);
int index = origTableCols.indexOf(origCol);
if (index != -1) {
copyIndexCols.add(copyTableCols.get(index));
}
}
}
private String getUniqueNIS(final Table orig) {
if (orig.getNameInSource() != null
&& !orig.getNameInSource().trim().equals(
StringConstants.EMPTY_STRING)) {
final StringBuffer buffer = new StringBuffer(orig.getNameInSource());
//Check to see if we have used this NIS. If so, append an index.
if (usedNIS.contains(buffer.toString())){
buffer.append(nextIndex);
}
usedNIS.add(buffer.toString());
return buffer.toString();
}
final StringBuffer buffer = new StringBuffer(MV_PREFIX);
buffer.append(nextIndex);
return buffer.toString();
}
public EList getModelResourceContents() {
EList eList = null;
try {
eList = this.materializedViewModel.getEmfResource().getContents();
} catch (ModelWorkspaceException err) {
TransformationPlugin.Util.log(err);
}
return eList;
}
public void addValue(final Object owner, final Object value, EList feature) {
try {
ModelerCore.getModelEditor().addValue(owner, value, feature);
} catch (ModelerCoreException err) {
TransformationPlugin.Util.log(err);
}
}
public void setVirtualTables(Collection<EObject> tables) {
this.virtualTables = new ArrayList(tables);
}
public Collection<EObject> getVirtualTables() {
return this.virtualTables;
}
public Object getTargetLocation() {
return targetLocation;
}
public void setTargetLocation(Object targetLocation) {
this.targetLocation = targetLocation;
try {
if( this.targetLocation instanceof Schema ) {
this.materializedViewModel = ModelUtil.getModel(this.targetLocation);
} else if( this.targetLocation instanceof IFile ) {
this.materializedViewModel = ModelUtil.getModelResource((IFile)this.targetLocation, true);
this.targetLocation = this.materializedViewModel.getParent();
} else if( this.targetLocation instanceof ModelResource ) {
this.materializedViewModel = (ModelResource)this.targetLocation;
}
} catch (ModelWorkspaceException err) {
TransformationPlugin.Util.log(err);
}
}
public void setMaterializedViewModel(ModelResource materializedViewModel) {
this.materializedViewModel = materializedViewModel;
}
public String getModelName() {
if( this.materializedViewModel != null ) {
return this.materializedViewModel.getPath().removeFileExtension().lastSegment();
}
return UNDEFINED;
}
public String getLocationName() {
if( this.targetLocation != null ) {
if( this.targetLocation instanceof ModelResource ) {
return getModelName();
}
return ModelerCore.getModelEditor().getName((Schema)this.targetLocation);
}
return UNDEFINED;
}
public IStatus getExecuteStatus() {
if( this.virtualTables == null || this.virtualTables.isEmpty() ) {
return new Status(IStatus.ERROR, TransformationPlugin.PLUGIN_ID,
TransformationPlugin.Util.getString("MVModelGenerator.noVirtualTablesSelectedError")); //$NON-NLS-1$
}
for( EObject vTable : this.virtualTables ) {
if( !(vTable instanceof Table) ) {
return new Status(IStatus.ERROR, TransformationPlugin.PLUGIN_ID,
TransformationPlugin.Util.getString("MVModelGenerator.nonTableSelectedError", //$NON-NLS-1$
ModelerCore.getModelEditor().getName(vTable)));
}
if( ((Table)vTable).isMaterialized() ) {
return new Status(IStatus.WARNING, TransformationPlugin.PLUGIN_ID,
TransformationPlugin.Util.getString("MVModelGenerator.viewAlreadyMaterializedError", ((Table)vTable).getName())); //$NON-NLS-1$
}
}
if( this.targetLocation == null ) {
return new Status(IStatus.ERROR, TransformationPlugin.PLUGIN_ID,
TransformationPlugin.Util.getString("MVModelGenerator.targetLocationIsNullError")); //$NON-NLS-1$
}
if( this.materializedViewModel == null ) {
return new Status(IStatus.ERROR, TransformationPlugin.PLUGIN_ID,
TransformationPlugin.Util.getString("MVModelGenerator.materializedViewModelIsNullError")); //$NON-NLS-1$
}
return OK_STATUS;
}
}