/*
* 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.ui.wizards;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.swing.tree.DefaultMutableTreeNode;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.teiid.core.designer.ModelerCoreException;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.container.ResourceFinder;
import org.teiid.designer.core.util.NewModelObjectHelperManager;
import org.teiid.designer.core.workspace.ModelResource;
import org.teiid.designer.metamodels.core.AnnotationContainer;
import org.teiid.designer.metamodels.core.ModelAnnotation;
import org.teiid.designer.metamodels.core.ModelImport;
import org.teiid.designer.metamodels.core.ModelType;
import org.teiid.designer.metamodels.transformation.TransformationContainer;
import org.teiid.designer.transformation.aspects.sql.SqlTransformationMappingRootSqlAspect;
import org.teiid.designer.ui.common.tree.TreeViewerUtil;
import org.teiid.designer.ui.common.widget.InheritanceCheckboxTreeViewer;
import org.teiid.designer.ui.explorer.ModelExplorerLabelProvider;
import org.teiid.designer.ui.viewsupport.ModelObjectUtilities;
/**
* StructuralCopyModelFeaturePopulator
*
* @since 8.0
*/
public class StructuralCopyModelFeaturePopulator implements IStructuralCopyTreePopulator {
private static String VIRTUAL_ROOT = "ROOT"; //$NON-NLS-1$
protected IFile sourceFile;
protected ICheckboxTreeViewerListenerController listenerController;
protected ModelResource targetModelResource;
/**
* Constructor
*
* @param sourceFile the source file
* @param listenerController controller for checkbox selection changes made in the tree viewer
*/
public StructuralCopyModelFeaturePopulator( IFile sourceFile ) {
super();
this.sourceFile = sourceFile;
}
/**
* Constructor
*
* @param sourceFile the source file
* @param listenerController controller for checkbox selection changes made in the tree viewer
*/
public StructuralCopyModelFeaturePopulator( IFile sourceFile,
ICheckboxTreeViewerListenerController listenerController ) {
super();
this.sourceFile = sourceFile;
this.listenerController = listenerController;
}
/**
* Populate the tree which will display selectable model features.
*
* @param viewer the tree viewer
* @param theModel ModelResource for the model
* @param targetIsVirtual flag indicating if target of copy is virtual
*/
@Override
public void populateModelFeaturesTree( TreeViewer viewer,
ModelResource theModel,
boolean targetIsVirtual ) {
ViewerFilter[] filters = viewer.getFilters();
StructuralCopyTreeViewerFilter filter = findStructuralCopyFilter(filters);
if (targetIsVirtual) {
if (filter == null) {
// need to add a filter:
viewer.addFilter(new StructuralCopyTreeViewerFilter());
} // endif
} else if (filter != null) {
// not virtual, remove any filter:
viewer.removeFilter(filter);
} // endif
StructuralCopyTreeContentProvider contentProvider = StructuralCopyTreeContentProvider.getInstance();
viewer.setContentProvider(contentProvider);
ModelExplorerLabelProvider labelProvider = new ModelExplorerLabelProvider();
viewer.setLabelProvider(labelProvider);
// clear out the input to make sure we can set the hash lookup flag:
viewer.setInput(null);
viewer.setUseHashlookup(true);
viewer.setInput(theModel);
}
private StructuralCopyTreeViewerFilter findStructuralCopyFilter( ViewerFilter[] filters ) {
for (int i = 0; i < filters.length; i++) {
ViewerFilter filter = filters[i];
if (filter instanceof StructuralCopyTreeViewerFilter) {
return (StructuralCopyTreeViewerFilter)filter;
} // endif
} // endfor
return null;
}
/**
* Copy the model to the target, only copying those nodes selected in the tree viewer.
*
* @param sourceModelResource modelResource containing the old information
* @param targetModelResource the target ModelResource
* @param viewer the tree viewer; root is the ModelResource
* @param extraProperties optional properties to tweak creation of objects.
* @param copyAllDescriptions option to copy or suppress copying all descriptions
* @param monitor a progress monitor
* @throws ModelerCoreException
*/
@Override
public void copyModel( ModelResource sourceModelResource,
ModelResource targetModelResource,
InheritanceCheckboxTreeViewer viewer,
Map extraProperties,
boolean copyAllDescriptions,
IProgressMonitor monitor ) {
// This method is being revoked due to inadequate design and implementation.
throw new UnsupportedOperationException();
}
/**
* Copy the model to the target, only copying those nodes selected in the tree viewer.
*
* @param sourceModelResource modelResource containing the old information
* @param targetModelResource the target ModelResource
* @param viewer the tree viewer; root is the ModelResource
* @param extraProperties optional properties to tweak creation of objects.
* @param copyAllDescriptions option to copy or suppress copying all descriptions
* @param monitor a progress monitor
* @throws ModelerCoreException
*/
@Override
public void copyModel( ModelResource sourceModelResource,
ModelResource targetModelResource,
Map extraProperties,
boolean copyAllDescriptions,
IProgressMonitor monitor ) throws ModelerCoreException {
List /*<EObject>*/allSourceRootContents = sourceModelResource.getEmfResource().getContents();
boolean sourceIsVirtual = sourceModelResource.getModelAnnotation().getModelType().equals(ModelType.VIRTUAL_LITERAL);
boolean targetIsVirtual = targetModelResource.getModelAnnotation().getModelType().equals(ModelType.VIRTUAL_LITERAL);
ModelType targetModelType = targetModelResource.getModelAnnotation().getModelType();
// Defect 24086 - Creating a Source model from a Virtual model requires we remove the Transformation Container from the
// equation.
List filteredChildren = new ArrayList(allSourceRootContents.size());
// Need to filter the first level children as below....
// JIRA Issue JBEDSP-257
// Add Descriptions to the filteredChildren if includeDescriptions = true
// Defect 24086 - Creating a Source model from a Virtual model requires we remove the Transformation Container from the
// equation.
for (Iterator iter = allSourceRootContents.iterator(); iter.hasNext();) {
EObject nextChild = (EObject)iter.next();
if (nextChild instanceof AnnotationContainer) {
if (copyAllDescriptions) {
filteredChildren.add(nextChild);
}
} else if (nextChild instanceof TransformationContainer) {
if (targetIsVirtual && sourceIsVirtual) {
filteredChildren.add(nextChild);
}
} else if (ModelObjectUtilities.isJdbcSource(nextChild)) {
if (!targetIsVirtual) {
filteredChildren.add(nextChild);
}
} else {
filteredChildren.add(nextChild);
}
}
Collection /*<EObject>*/sourceFirstLevelChildrenCopies = null;
// We cannot modify the source model, so create a deep copy of it
try {
sourceFirstLevelChildrenCopies = ModelerCore.getModelEditor().copyAll(filteredChildren);
// don't allow 2 ModelAnnotations so if there is a ModelAnnotation in the target model remove it exists
List targetChildren = targetModelResource.getEmfResource().getContents();
for (int numKids = targetChildren.size(), i = 0; i < numKids; ++i) {
Object kid = targetChildren.get(i);
if (kid instanceof ModelAnnotation) {
targetChildren.remove(kid);
break;
}
}
} catch (ModelerCoreException ex) {
throw ex;
}
/*
* Replicating the model objects is not quite good enough since the transformations may
* still contain the name of the source model.
* Need to replace those references with the new name.
*/
SqlTransformationMappingRootSqlAspect.replaceTransformationLiteral(
sourceFirstLevelChildrenCopies,
sourceModelResource.getItemName(),
targetModelResource.getItemName());
// just add the nodes to the target:
targetModelResource.getEmfResource().getContents().addAll(sourceFirstLevelChildrenCopies);
// Need to re-set the model type here.
ModelAnnotation modelAnnotation = targetModelResource.getModelAnnotation();
modelAnnotation.setModelType(targetModelType);
/*
* Need to check that all the model imports are valid
* When a model is copied it seems to leave behind imports referencing
* models relative to the old location which can be invalid for the new location.
*
* Remove those import statements that are invalid for the new location.
*/
EList imports = modelAnnotation.getModelImports();
if (imports != null) {
ResourceFinder resourceFinder;
try {
resourceFinder = ModelerCore.getModelContainer().getResourceFinder();
} catch (CoreException ex) {
throw new ModelerCoreException(ex);
}
//
// Iterator through the imports and use the resource finder
// to determine if the uri location of the import model is still
// valid relative to the target model resource location
//
List<Object> mImports = Arrays.asList(imports.toArray());
for (Object mImport : mImports) {
ModelImport modelImport = (ModelImport) mImport;
URI uri = URI.createURI(modelImport.getModelLocation());
if (resourceFinder.isBuiltInResource(uri))
continue;
Resource resource = resourceFinder.findByWorkspaceUri(uri, targetModelResource.getEmfResource());
if (resource == null) {
// The resource location is invalid so remove it
imports.remove(mImport);
}
}
}
// Now we need to check if virtual, then call the NewModelObjectHelper .....
if (targetIsVirtual) {
try {
List eObjects = targetModelResource.getEObjects();
for (Iterator iter = eObjects.iterator(); iter.hasNext();) {
NewModelObjectHelperManager.helpCreate(iter.next(), extraProperties);
}
} catch (ModelerCoreException err) {
throw err;
}
}
}
protected DefaultMutableTreeNode[] getChildrenOfNode( DefaultMutableTreeNode node ) {
int numChildren = node.getChildCount();
DefaultMutableTreeNode[] children = new DefaultMutableTreeNode[numChildren];
for (int i = 0; i < numChildren; i++) {
children[i] = (DefaultMutableTreeNode)node.getChildAt(i);
}
return children;
}
protected Object getParent( Object curNode,
Collection /*<EObject>*/sourceFirstLevelChildrenCopies ) {
Object parent;
if (curNode == VIRTUAL_ROOT) {
parent = null;
} else if (sourceFirstLevelChildrenCopies.contains(curNode)) {
parent = VIRTUAL_ROOT;
} else {
EObject obj = (EObject)curNode;
parent = obj.eContainer();
}
return parent;
}
protected Collection getChildren( Object curNode,
Collection /*<EObject>*/sourceFirstLevelChildrenCopies ) {
Collection children;
if (curNode == VIRTUAL_ROOT) {
children = sourceFirstLevelChildrenCopies;
} else {
EObject obj = (EObject)curNode;
children = obj.eContents();
}
return children;
}
protected void deleteChildAtIndex( Object parent,
int index,
Collection /*<EObject>*/sourceFirstLevelChildrenCopies ) throws ModelerCoreException {
Collection children;
if (parent == VIRTUAL_ROOT) {
children = sourceFirstLevelChildrenCopies;
} else {
EObject obj = (EObject)parent;
children = obj.eContents();
}
Iterator it = children.iterator();
for (int i = 0; i < index; i++) {
it.next();
}
Object childToRemove = it.next();
if (parent == VIRTUAL_ROOT) {
sourceFirstLevelChildrenCopies.remove(childToRemove);
}
ModelerCore.getModelEditor().delete((EObject)childToRemove);
}
protected Object getChildAtIndex( Object parent,
int index,
Collection /*<EObject>*/sourceFirstLevelChildrenCopies ) {
Collection children;
if (parent == VIRTUAL_ROOT) {
children = sourceFirstLevelChildrenCopies;
} else {
EObject obj = (EObject)parent;
children = obj.eContents();
}
Iterator it = children.iterator();
for (int i = 0; i < index; i++) {
it.next();
}
Object child = it.next();
return child;
}
/**
* Return the root of the tree as pared down to exclude any unneeded nodes. That is, only nodes which themselves are checked
* or have any descendants that are checked will be included. Using a {@link javax.swing.tree.DefaultMutableTreeNode} for each
* node in this tree because this class contains the simple logic needed (methods to get parent and get ordered children for
* each node) without having any unncessary associated GUI, and because the getUserObject() method is needed to store the
* position index for each node.
*
* @return the root of the pared down tree, where all nodes are represented as a
* {@link javax.swing.tree.DefaultMutableTreeNode}.
*/
protected DefaultMutableTreeNode getParedTreeRoot( InheritanceCheckboxTreeViewer viewer,
ModelResource modelResource ) {
DefaultMutableTreeNode root = new DefaultMutableTreeNode(new IndexAndObject(0, modelResource));
DefaultMutableTreeNode curNode = root;
boolean done = false;
while (!done) {
IndexAndObject io = (IndexAndObject)curNode.getUserObject();
Object obj = io.getObject();
Object[] children = ((ITreeContentProvider)viewer.getContentProvider()).getChildren(obj);
int childIndex = firstRequiredChildIndex(viewer, 0, children);
if (childIndex >= 0) {
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(new IndexAndObject(childIndex, children[childIndex]));
curNode.add(newNode);
curNode = newNode;
} else {
boolean nextNodeFound = false;
while ((!nextNodeFound) && (!done)) {
io = (IndexAndObject)curNode.getUserObject();
Object prevNodeContentObject = io.getObject();
curNode = (DefaultMutableTreeNode)curNode.getParent();
if (curNode == null) {
done = true;
} else {
io = (IndexAndObject)curNode.getUserObject();
Object curNodeContentObject = io.getObject();
children = ((ITreeContentProvider)viewer.getContentProvider()).getChildren(curNodeContentObject);
int prevNodeIndex = indexOf(prevNodeContentObject, children);
childIndex = firstRequiredChildIndex(viewer, prevNodeIndex + 1, children);
if (childIndex >= 0) {
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(new IndexAndObject(childIndex,
children[childIndex]));
curNode.add(newNode);
curNode = newNode;
nextNodeFound = true;
}
}
}
}
}
return root;
}
protected int firstRequiredChildIndex( InheritanceCheckboxTreeViewer viewer,
int startingIndex,
Object[] children ) {
int firstRequiredIndex = -1;
int curIndex = startingIndex;
while ((curIndex < children.length) && (firstRequiredIndex < 0)) {
Object curChild = children[curIndex];
if (viewer.getChecked(curChild) || TreeViewerUtil.anyDescendantChecked(viewer, curChild)) {
firstRequiredIndex = curIndex;
} else {
curIndex++;
}
}
return firstRequiredIndex;
}
protected int indexOf( Object obj,
Object[] array ) {
int index = 0;
while (obj != array[index]) {
index++;
}
return index;
}
protected void insertInitialFirstLevelChildren( DefaultMutableTreeNode root,
List /*<EObject>*/firstLevelChildren,
int numToInsert ) {
for (int i = 0; i < numToInsert; i++) {
IndexAndObject io = new IndexAndObject(i, firstLevelChildren.get(i));
DefaultMutableTreeNode child = new DefaultMutableTreeNode(io);
root.insert(child, i);
}
}
protected void adjustIndexOfFirstLevelChildren( DefaultMutableTreeNode root,
int increment ) {
DefaultMutableTreeNode[] children = getChildrenOfNode(root);
for (int i = 0; i < children.length; i++) {
IndexAndObject io = (IndexAndObject)children[i].getUserObject();
io.setIndex(io.getIndex() + increment);
}
}
protected Object getObjectForTreeNodeUserObject( Object treeNodeUserObject ) {
IndexAndObject io = (IndexAndObject)treeNodeUserObject;
return io.getObject();
}
}// end StructuralCopyModelFeaturePopulator
/**
* Auxilliary data class
*/
class IndexAndObject {
private int index;
private Object object;
public IndexAndObject( int index,
Object object ) {
super();
this.index = index;
this.object = object;
}
public int getIndex() {
return index;
}
public void setIndex( int newIndex ) {
index = newIndex;
}
public Object getObject() {
return object;
}
@Override
public String toString() {
String str = "index=" + index //$NON-NLS-1$
+ ", object=" + object; //$NON-NLS-1$
return str;
}
}// end IndexAndObject