/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.mappingsmodel.project;
import java.io.File;
import java.io.FileNotFoundException;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.descriptors.InheritancePolicy;
import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.oxm.mappings.XMLCompositeDirectCollectionMapping;
import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping;
import org.eclipse.persistence.oxm.mappings.XMLDirectMapping;
import org.eclipse.persistence.sessions.DatabaseRecord;
import org.eclipse.persistence.sessions.DatasourceLogin;
import org.eclipse.persistence.sessions.Project;
import org.eclipse.persistence.sessions.Record;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.sessions.factories.ProjectClassGenerator;
import org.eclipse.persistence.sessions.factories.XMLProjectWriter;
import org.eclipse.persistence.tools.workbench.mappingsmodel.MWModel;
import org.eclipse.persistence.tools.workbench.mappingsmodel.ProjectSubFileComponentContainer;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.DescriptorCreationFailureEvent;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.DescriptorCreationFailureListener;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.DescriptorStringHolder;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.InterfaceDescriptorCreationException;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWMappingDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.relational.MWInterfaceDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.relational.MWAggregateMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.ExternalClassLoadFailureContainer;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.ExternalClassLoadFailureEvent;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClass;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClassAttribute;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClassRepository;
import org.eclipse.persistence.tools.workbench.mappingsmodel.project.relational.MWRelationalProject;
import org.eclipse.persistence.tools.workbench.mappingsmodel.project.xml.MWEisProject;
import org.eclipse.persistence.tools.workbench.mappingsmodel.project.xml.MWOXProject;
import org.eclipse.persistence.tools.workbench.mappingsmodel.spi.SPIManager;
import org.eclipse.persistence.tools.workbench.mappingsmodel.spi.meta.ExternalClassDescription;
import org.eclipse.persistence.tools.workbench.mappingsmodel.spi.meta.ExternalClassNotFoundException;
import org.eclipse.persistence.tools.workbench.utility.ClassTools;
import org.eclipse.persistence.tools.workbench.utility.Classpath;
import org.eclipse.persistence.tools.workbench.utility.CollectionTools;
import org.eclipse.persistence.tools.workbench.utility.ManifestInterrogator;
import org.eclipse.persistence.tools.workbench.utility.XMLTools;
import org.eclipse.persistence.tools.workbench.utility.ManifestInterrogator.Defaults;
import org.eclipse.persistence.tools.workbench.utility.events.ChangeNotifier;
import org.eclipse.persistence.tools.workbench.utility.events.DefaultChangeNotifier;
import org.eclipse.persistence.tools.workbench.utility.io.FileTools;
import org.eclipse.persistence.tools.workbench.utility.iterators.CloneIterator;
import org.eclipse.persistence.tools.workbench.utility.iterators.CompositeIterator;
import org.eclipse.persistence.tools.workbench.utility.iterators.FilteringIterator;
import org.eclipse.persistence.tools.workbench.utility.iterators.NullIterator;
import org.eclipse.persistence.tools.workbench.utility.iterators.TransformationIterator;
import org.eclipse.persistence.tools.workbench.utility.node.Node;
import org.eclipse.persistence.tools.workbench.utility.string.AffixStrippingPartialStringComparatorEngine;
import org.eclipse.persistence.tools.workbench.utility.string.ExhaustivePartialStringComparatorEngine;
import org.eclipse.persistence.tools.workbench.utility.string.PartialStringComparator;
import org.eclipse.persistence.tools.workbench.utility.string.PartialStringComparatorEngine;
import org.w3c.dom.Document;
public abstract class MWProject
extends MWModel
implements ProjectSubFileComponentContainer
{
private volatile String name;
public static final String NAME_PROPERTY = "name";
/**
* transient attribute configured upon construction;
* does not change
*/
private SPIManager spiManager;
private Collection descriptors;
public static final String DESCRIPTORS_COLLECTION = "descriptors";
private MWClassRepository classRepository;
private MWProjectDefaultsPolicy defaultsPolicy;
/**
* deployment xml
*/
private volatile String deploymentXMLFileName;
public static final String DEPLOYMENT_XML_FILE_NAME_PROPERTY = "deploymentXMLFileName";
/**
* project source code settings
*/
private volatile String projectSourceClassName;
public static final String PROJECT_SOURCE_CLASS_NAME_PROPERTY = "projectSourceClassName";
private volatile String projectSourceDirectoryName;
public static final String PROJECT_SOURCE_DIRECTORY_NAME_PROPERTY = "projectSourceDirectoryName";
/**
* model source code settings
*/
private volatile String modelSourceDirectoryName;
public static final String MODEL_SOURCE_DIRECTORY_NAME_PROPERTY = "modelSourceDirectoryName";
/**
* transient attribute whose value is determined by where the project
* was read from or where it was last written
*/
private volatile File saveDirectory;
public static final String SAVE_DIRECTORY_PROPERTY = "saveDirectory";
/**
* these descriptor names are read in by TopLink and are then
* used and managed by the IOManager;
* DO NOT use them for anything else ~bjv
*/
private Collection descriptorNames;
private static final String DESCRIPTOR_NAMES_COLLECTION = "descriptorNames";
/**
* transient attribute used to determine if the version has
* changed; see #postProjectBuild()
*/
private String version;
/**
* transient attribute used only to inform the user
* that legacy projects must be saved in the new file format
*/
private volatile boolean legacyProject;
/**
* transient attribute used to forward change notifications to listeners;
* by default it simply forwards the notifications,
* but it is configured by the UI to forward the notifications on the
* AWT event dispatch thread
*/
private ChangeNotifier changeNotifier;
/**
* transient attribute used to generate problems - does nothing by default;
* but is configured by the UI to generate problems in a separate thread
*/
private Validator validator;
/**
* This flag is set to true when the project is being validated.
*/
private volatile boolean validating;
public static final String VALIDATING_PROPERTY = "validating";
//This is used for project weaving option which affects indirection validation for mappings
private volatile boolean usesWeaving;
public final static String USES_WEAVING_PROPERTY = "usesWeaving";
// ********** static fields **********
/**
* The name of the root node of the XML document in the
* project file (this is used to detect old projects).
* Unfortunately, this has changed over time....
* If this changes, update the appropriate code in ProjectReader
* and MWProject.
*/
public static final String CURRENT_PROJECT_ROOT_ELEMENT_NAME = "project";
/**
* The name of the XML element in the project file that
* contains the schema version (which is used to detect old projects).
* Unfortunately, this has changed over time....
* If this changes, update the appropriate code in ProjectReader
* and MWProject.
*/
public static final String CURRENT_SCHEMA_VERSION_ELEMENT_NAME = "schema-version";
/**
* The current schema version number (major.minor).
* Change the minor number when the schema changes but is still
* backward-compatible. Change the major number when the schema
* changes and is no longer backward-compatible.
* If the major number changes, update the appropriate code in ProjectReader
* and MWProject.
*/
public static final String CURRENT_SCHEMA_VERSION = "7.0";
/*
* The above static values are used to discover the schema version
* in an XML document that looks like this (old projects will look slightly different):
* <project>
* <name>Foo Project</name>
* <product-version>11.1.0.0</product-version>
* <schema-version>7.0</schema-version>
* ...
* </project>
*/
// used by I/O Manager
private static final String SUB_DIRECTORY_NAME = "descriptors";
/** The project-specific file name extension - used by the I/O manager. */
public static final String FILE_NAME_EXTENSION = ".mwp";
private static final ManifestInterrogator MANIFEST_INTERROGATOR = new ManifestInterrogator(MWProject.class, new LocalManifestDefaults());
// ********** static methods **********
public static XMLDescriptor buildDescriptor() {
XMLDescriptor descriptor = new XMLDescriptor();
descriptor.setJavaClass(MWProject.class);
descriptor.setDefaultRootElement(CURRENT_PROJECT_ROOT_ELEMENT_NAME);
InheritancePolicy ip = (InheritancePolicy) descriptor.getInheritancePolicy();
ip.setClassIndicatorFieldName("@type");
ip.addClassIndicator(MWRelationalProject.class, "relational");
ip.addClassIndicator(MWOXProject.class, "o-x");
ip.addClassIndicator(MWEisProject.class, "eis");
descriptor.addDirectMapping("name", "name/text()");
((XMLDirectMapping) descriptor.addDirectMapping("comment", "comment/text()")).setNullValue("");
descriptor.addDirectMapping("version", "getProductVersionForTopLink", "setProductVersionForTopLink", "product-version/text()");
//TODO - billy : what's the best way to handle this?
descriptor.addDirectMapping("schemaVersion", "getSchemaVersionForTopLink", "setSchemaVersionForTopLink", CURRENT_SCHEMA_VERSION_ELEMENT_NAME + "/text()");
XMLCompositeObjectMapping repositoryMapping = new XMLCompositeObjectMapping();
repositoryMapping.setAttributeName("classRepository");
repositoryMapping.setReferenceClass(MWClassRepository.class);
repositoryMapping.setXPath("class-repository");
descriptor.addMapping(repositoryMapping);
XMLCompositeDirectCollectionMapping descriptorNamesMapping = new XMLCompositeDirectCollectionMapping();
descriptorNamesMapping.setAttributeName("descriptorNames");
descriptorNamesMapping.setSetMethodName("setDescriptorNamesForTopLink");
descriptorNamesMapping.setGetMethodName("getDescriptorNamesForTopLink");
descriptorNamesMapping.useCollectionClass(HashSet.class);
descriptorNamesMapping.setXPath("descriptor-names/descriptor-name/text()");
descriptor.addMapping(descriptorNamesMapping);
XMLCompositeObjectMapping defaultsPolicyMapping = new XMLCompositeObjectMapping();
defaultsPolicyMapping.setAttributeName("defaultsPolicy");
defaultsPolicyMapping.setReferenceClass(MWProjectDefaultsPolicy.class);
defaultsPolicyMapping.setXPath("defaults-policy");
descriptor.addMapping(defaultsPolicyMapping);
XMLDirectMapping deploymentXMLFileNameMapping = new XMLDirectMapping();
deploymentXMLFileNameMapping.setAttributeName("deploymentXMLFileName");
deploymentXMLFileNameMapping.setSetMethodName("setDeploymentXMLFileNameForTopLink");
deploymentXMLFileNameMapping.setGetMethodName("getDeploymentXMLFileNameForTopLink");
deploymentXMLFileNameMapping.setXPath("deployment-xml-file/text()");
deploymentXMLFileNameMapping.setNullValue("");
descriptor.addMapping(deploymentXMLFileNameMapping);
XMLDirectMapping projectSourceClassNameMapping = new XMLDirectMapping();
projectSourceClassNameMapping.setAttributeName("projectSourceClassName");
projectSourceClassNameMapping.setXPath("project-source/class/text()");
projectSourceClassNameMapping.setNullValue("");
descriptor.addMapping(projectSourceClassNameMapping);
XMLDirectMapping projectSourceDirectoryNameMapping = new XMLDirectMapping();
projectSourceDirectoryNameMapping.setAttributeName("projectSourceDirectoryName");
projectSourceDirectoryNameMapping.setSetMethodName("setProjectSourceDirectoryNameForTopLink");
projectSourceDirectoryNameMapping.setGetMethodName("getProjectSourceDirectoryNameForTopLink");
projectSourceDirectoryNameMapping.setXPath("project-source/directory/text()");
projectSourceDirectoryNameMapping.setNullValue("");
descriptor.addMapping(projectSourceDirectoryNameMapping);
XMLDirectMapping modelSourceDirectoryNameMapping = new XMLDirectMapping();
modelSourceDirectoryNameMapping.setAttributeName("modelSourceDirectoryName");
modelSourceDirectoryNameMapping.setSetMethodName("setModelSourceDirectoryNameForTopLink");
modelSourceDirectoryNameMapping.setGetMethodName("getModelSourceDirectoryNameForTopLink");
modelSourceDirectoryNameMapping.setXPath("model-source/directory/text()");
modelSourceDirectoryNameMapping.setNullValue("");
descriptor.addMapping(modelSourceDirectoryNameMapping);
XMLDirectMapping useWeavingMapping = new XMLDirectMapping();
useWeavingMapping.setAttributeName("usesWeaving");
useWeavingMapping.setXPath("use-weaving/text()");
useWeavingMapping.setNullValue(Boolean.FALSE);
descriptor.addMapping(useWeavingMapping);
return descriptor;
}
public static XMLDescriptor buildLegacy60Descriptor() {
XMLDescriptor descriptor = MWModel.legacy60BuildStandardDescriptor();
descriptor.setJavaClass(MWProject.class);
descriptor.setDefaultRootElement(CURRENT_PROJECT_ROOT_ELEMENT_NAME);
InheritancePolicy ip = (InheritancePolicy) descriptor.getInheritancePolicy();
ip.setClassIndicatorFieldName("@type");
ip.addClassIndicator(MWRelationalProject.class, "relational");
ip.addClassIndicator(MWOXProject.class, "o-x");
ip.addClassIndicator(MWEisProject.class, "eis");
descriptor.addDirectMapping("name", "name/text()");
((XMLDirectMapping) descriptor.addDirectMapping("comment", "comment/text()")).setNullValue("");
descriptor.addDirectMapping("version", "getProductVersionForTopLink", "setProductVersionForTopLink", "product-version/text()");
//TODO - billy : what's the best way to handle this?
descriptor.addDirectMapping("schemaVersion", "getSchemaVersionForTopLink", "setSchemaVersionForTopLink", CURRENT_SCHEMA_VERSION_ELEMENT_NAME + "/text()");
XMLCompositeObjectMapping repositoryMapping = new XMLCompositeObjectMapping();
repositoryMapping.setAttributeName("classRepository");
repositoryMapping.setReferenceClass(MWClassRepository.class);
repositoryMapping.setXPath("class-repository");
descriptor.addMapping(repositoryMapping);
XMLCompositeDirectCollectionMapping descriptorNamesMapping = new XMLCompositeDirectCollectionMapping();
descriptorNamesMapping.setAttributeName("descriptorNames");
descriptorNamesMapping.setSetMethodName("setDescriptorNamesForTopLink");
descriptorNamesMapping.setGetMethodName("getDescriptorNamesForTopLink");
descriptorNamesMapping.useCollectionClass(HashSet.class);
descriptorNamesMapping.setXPath("descriptor-names/descriptor-name/text()");
descriptor.addMapping(descriptorNamesMapping);
XMLCompositeObjectMapping defaultsPolicyMapping = new XMLCompositeObjectMapping();
defaultsPolicyMapping.setAttributeName("defaultsPolicy");
defaultsPolicyMapping.setReferenceClass(MWProjectDefaultsPolicy.class);
defaultsPolicyMapping.setXPath("defaults-policy");
descriptor.addMapping(defaultsPolicyMapping);
XMLDirectMapping deploymentXMLFileNameMapping = new XMLDirectMapping();
deploymentXMLFileNameMapping.setAttributeName("deploymentXMLFileName");
deploymentXMLFileNameMapping.setSetMethodName("setDeploymentXMLFileNameForTopLink");
deploymentXMLFileNameMapping.setGetMethodName("getDeploymentXMLFileNameForTopLink");
deploymentXMLFileNameMapping.setXPath("deployment-xml-file/text()");
deploymentXMLFileNameMapping.setNullValue("");
descriptor.addMapping(deploymentXMLFileNameMapping);
XMLDirectMapping projectSourceClassNameMapping = new XMLDirectMapping();
projectSourceClassNameMapping.setAttributeName("projectSourceClassName");
projectSourceClassNameMapping.setXPath("project-source/class/text()");
projectSourceClassNameMapping.setNullValue("");
descriptor.addMapping(projectSourceClassNameMapping);
XMLDirectMapping projectSourceDirectoryNameMapping = new XMLDirectMapping();
projectSourceDirectoryNameMapping.setAttributeName("projectSourceDirectoryName");
projectSourceDirectoryNameMapping.setSetMethodName("setProjectSourceDirectoryNameForTopLink");
projectSourceDirectoryNameMapping.setGetMethodName("getProjectSourceDirectoryNameForTopLink");
projectSourceDirectoryNameMapping.setXPath("project-source/directory/text()");
projectSourceDirectoryNameMapping.setNullValue("");
descriptor.addMapping(projectSourceDirectoryNameMapping);
XMLDirectMapping modelSourceDirectoryNameMapping = new XMLDirectMapping();
modelSourceDirectoryNameMapping.setAttributeName("modelSourceDirectoryName");
modelSourceDirectoryNameMapping.setSetMethodName("setModelSourceDirectoryNameForTopLink");
modelSourceDirectoryNameMapping.setGetMethodName("getModelSourceDirectoryNameForTopLink");
modelSourceDirectoryNameMapping.setXPath("model-source/directory/text()");
modelSourceDirectoryNameMapping.setNullValue("");
descriptor.addMapping(modelSourceDirectoryNameMapping);
return descriptor;
}
// ********** constructors **********
/**
* Default constructor - for TopLink use only.
*/
protected MWProject() {
super();
}
protected MWProject(String name, SPIManager spiManager) {
// projects are the only things that can go without a parent
super(null);
this.name = name;
this.spiManager = spiManager;
}
// ********** initialization **********
/**
* initialize transient state
*/
protected void initialize() {
super.initialize();
this.descriptors = new Vector(); // descriptors are not mapped directly
this.changeNotifier = DefaultChangeNotifier.instance();
this.validator = NULL_VALIDATOR; // clients will replace this if appropriate
this.validating = false;
}
/**
* initialize persistent state
*/
protected void initialize(Node parent) {
super.initialize(parent);
this.descriptorNames = new HashSet();
this.classRepository = new MWClassRepository(this);
this.defaultsPolicy = this.buildDefaultsPolicy();
this.deploymentXMLFileName = "";
this.projectSourceClassName = "";
this.projectSourceDirectoryName = "";
this.modelSourceDirectoryName = "";
this.usesWeaving = false;
}
protected abstract MWProjectDefaultsPolicy buildDefaultsPolicy();
protected void checkParent(Node parent) {
// only Projects can lack a parent
if (parent != null) {
throw new IllegalArgumentException("An MWProject should not have a parent");
}
}
protected void addChildrenTo(List children) {
super.addChildrenTo(children);
synchronized (this.descriptors) { children.addAll(this.descriptors); }
children.add(this.classRepository);
children.add(this.defaultsPolicy);
}
// ********** accessors **********
// ***** name
public String getName() {
return this.name;
}
public void setName(String name) {
Object old = this.name;
this.name = name;
this.firePropertyChanged(NAME_PROPERTY, old, name);
}
// ***** SPI manager
public SPIManager getSPIManager() {
return this.spiManager;
}
// ***** descriptors
public Iterator descriptors() {
return new CloneIterator(this.descriptors) {
protected void remove(Object current) {
MWProject.this.removeDescriptor((MWDescriptor) current);
}
};
}
/** private - called by I/O Manager */
private void setDescriptors(Collection descriptors) {
this.descriptors = descriptors;
}
public int descriptorsSize() {
return this.descriptors.size();
}
/** private - descriptors cannot be added directly */
protected MWDescriptor addDescriptor(MWDescriptor descriptor) {
descriptor.applyAdvancedPolicyDefaults(this.getDefaultsPolicy());
this.addItemToCollection(descriptor, this.descriptors, DESCRIPTORS_COLLECTION);
return descriptor;
}
public MWDescriptor addDescriptorForType(MWClass type) throws InterfaceDescriptorCreationException {
return this.addDescriptor(this.createDescriptorForType(type));
}
protected abstract MWDescriptor createDescriptorForType(MWClass type) throws InterfaceDescriptorCreationException;
public void removeDescriptor(MWDescriptor descriptor) {
this.removeNodeFromCollection(descriptor, this.descriptors, DESCRIPTORS_COLLECTION);
}
public void removeDescriptorForType(MWClass type) {
MWDescriptor descriptor = this.descriptorForType(type);
if (descriptor != null) {
this.removeDescriptor(descriptor);
}
}
public void removeDescriptors(Collection descs) {
this.removeNodesFromCollection(descs, this.descriptors, DESCRIPTORS_COLLECTION);
}
public void removeDescriptors(Iterator descs) {
this.removeNodesFromCollection(descs, this.descriptors, DESCRIPTORS_COLLECTION);
}
public void replaceDescriptor(MWDescriptor oldDescriptor, MWDescriptor newDescriptor) {
// don't want to trigger cascading #nodeRemoved(Node) - only need to trigger #descriptorReplaced()
this.removeItemFromCollection(oldDescriptor, this.descriptors, DESCRIPTORS_COLLECTION);
this.descriptorReplaced(oldDescriptor, newDescriptor);
}
// ***** class repository
/**
* this is used by MWModel - allows us
* to make classRepository() final
*/
public MWClassRepository getClassRepository() {
return this.classRepository;
}
// ***** defaults policy
public MWProjectDefaultsPolicy getDefaultsPolicy() {
return this.defaultsPolicy;
}
// ***** deployment xml
public String getDeploymentXMLFileName() {
return this.deploymentXMLFileName;
}
public void setDeploymentXMLFileName(String deploymentXMLFileName) {
if (deploymentXMLFileName == null) {
throw new NullPointerException();
}
Object old = this.deploymentXMLFileName;
this.deploymentXMLFileName = deploymentXMLFileName;
this.firePropertyChanged(DEPLOYMENT_XML_FILE_NAME_PROPERTY, old, deploymentXMLFileName);
}
// ***** project source
public String getProjectSourceClassName() {
return this.projectSourceClassName;
}
public void setProjectSourceClassName(String projectSourceClassName) {
Object old = this.projectSourceClassName;
this.projectSourceClassName = projectSourceClassName;
this.firePropertyChanged(PROJECT_SOURCE_CLASS_NAME_PROPERTY, old, projectSourceClassName);
}
public String getProjectSourceDirectoryName() {
return this.projectSourceDirectoryName;
}
public void setProjectSourceDirectoryName(String projectSourceDirectoryName) {
if (projectSourceDirectoryName == null) {
throw new NullPointerException();
}
Object old = this.projectSourceDirectoryName;
this.projectSourceDirectoryName = projectSourceDirectoryName;
this.firePropertyChanged(PROJECT_SOURCE_DIRECTORY_NAME_PROPERTY, old, projectSourceDirectoryName);
}
// ***** model source
public String getModelSourceDirectoryName() {
return this.modelSourceDirectoryName;
}
public void setModelSourceDirectoryName(String modelSourceDirectoryName) {
if (modelSourceDirectoryName == null) {
throw new NullPointerException();
}
Object old = this.modelSourceDirectoryName;
this.modelSourceDirectoryName = modelSourceDirectoryName;
this.firePropertyChanged(MODEL_SOURCE_DIRECTORY_NAME_PROPERTY, old, modelSourceDirectoryName);
}
// ***** save directory
public File getSaveDirectory() {
return this.saveDirectory;
}
/**
* if the save directory changes, we mark the entire
* project dirty so it is written out in the new directory
*/
public void setSaveDirectory(File saveDirectory) {
Object old = this.saveDirectory;
this.saveDirectory = saveDirectory;
this.firePropertyChanged(SAVE_DIRECTORY_PROPERTY, old, saveDirectory);
if (this.attributeValueHasChanged(old, saveDirectory)) {
this.markEntireBranchDirty();
}
}
// ***** legacy project
public boolean isLegacyProject() {
return this.legacyProject;
}
public void setIsLegacyProject(boolean legacyProject) {
this.legacyProject = legacyProject;
}
// ***** changeNotifier
/**
* as the root node, we must implement this method
*/
public ChangeNotifier getChangeNotifier() {
return this.changeNotifier;
}
/**
* allow clients to install another change notifier
*/
public void setChangeNotifier(ChangeNotifier changeNotifier) {
this.changeNotifier = changeNotifier;
}
// ***** validator
/**
* as the root node, we must implement this method
*/
public Validator getValidator() {
return this.validator;
}
/**
* allow clients to install an active validator
*/
public void setValidator(Validator validator) {
this.validator = validator;
}
// ***** validating
public final boolean isValidating() {
return this.validating;
}
private void setIsValidating(boolean validating) {
boolean old = this.validating;
this.validating = validating;
this.firePropertyChanged(VALIDATING_PROPERTY, old, validating);
}
// ********** descriptors and mappings **********
/**
* return only the "mapping" descriptors, as opposed to "interface" descriptors
*/
public Iterator mappingDescriptors() {
return new FilteringIterator(this.descriptors()) {
protected boolean accept(Object next) {
return next instanceof MWMappingDescriptor;
}
};
}
public Iterator interfaceDescriptorsThatImplement(MWMappingDescriptor descriptor) {
return NullIterator.instance();
}
public Iterator descriptorsInPackage(final String packageName) {
return new FilteringIterator(this.descriptors()) {
protected boolean accept(Object next) {
return ((MWDescriptor) next).packageName().equals(packageName);
}
};
}
/**
* Attempt to either refresh the type and add a descriptor or, if a descriptor exists already,
* refresh the descriptor's type. Return the refresh failures.
*/
public ExternalClassLoadFailureContainer addDescriptorsForExternalClassDescriptions(Iterator externalClassDescriptions, DescriptorCreationFailureListener listener) {
ExternalClassLoadFailureContainer failures = new ExternalClassLoadFailureContainer();
while (externalClassDescriptions.hasNext()) {
ExternalClassDescription exClassDescription = (ExternalClassDescription) externalClassDescriptions.next();
try {
this.classRepository.refreshTypeFor(exClassDescription);
} catch (ExternalClassNotFoundException ex) {
failures.externalClassLoadFailure(new ExternalClassLoadFailureEvent(this, exClassDescription.getName(), ex));
continue; // skip to the next external class description
}
MWClass type = this.typeNamed(exClassDescription.getName());
MWDescriptor descriptor = this.descriptorForType(type);
if (descriptor == null) {
try {
descriptor = this.addDescriptorForType(type);
} catch (InterfaceDescriptorCreationException ex) {
listener.descriptorCreationFailure(new DescriptorCreationFailureEvent(this, type.getName(), "DESCRIPTOR_CREATION_ERROR_MESSAGE"));
}
}
}
return failures;
}
public Iterator activeDescriptors() {
return new FilteringIterator(this.descriptors()) {
public boolean accept(Object next) {
return ((MWDescriptor) next).isActive();
}
};
}
/**
* Returns a descriptor for a given type. This will NOT create the descriptor.
*/
public MWDescriptor descriptorForType(MWClass type) {
synchronized (this.descriptors) {
for (Iterator stream = this.descriptors.iterator(); stream.hasNext(); ) {
MWDescriptor descriptor = (MWDescriptor) stream.next();
if (descriptor.getMWClass() == type) {
return descriptor;
}
}
}
return null;
}
public MWDescriptor descriptorForTypeNamed(String typeName) {
return this.descriptorForType(this.typeNamed(typeName));
}
public MWDescriptor descriptorNamed(String descriptorName) {
synchronized (this.descriptors) {
for (Iterator stream = this.descriptors.iterator(); stream.hasNext(); ) {
MWDescriptor descriptor = (MWDescriptor) stream.next();
if (descriptor.getName().equals(descriptorName)) {
return descriptor;
}
}
}
return null;
}
public void implementorsChangedFor(MWInterfaceDescriptor descriptor) {
for (Iterator stream = this.mappingDescriptors(); stream.hasNext(); ) {
((MWMappingDescriptor) stream.next()).implementorsChangedFor(descriptor);
}
}
/**
* this is used by MWModel - allows us
* to make descriptorRepository() final
*/
public MWProject getDescriptorRepository() {
return this;
}
/**
* return all the mappings in the project's active descriptors
* that have not been marked "read-only"
*/
public Iterator allWriteableMappings() {
return new FilteringIterator(this.allMappings()) {
protected boolean accept(Object next) {
return ! ((MWMapping) next).isReadOnly();
}
};
}
/**
* return all the mappings in the project's active descriptors
*/
public Iterator allMappings() {
return new CompositeIterator(
new TransformationIterator(this.activeDescriptors()) {
protected Object transform(Object next) {
return ((MWDescriptor) next).mappings();
}
}
);
}
public DescriptorStringHolder[] descriptorStringHolders() {
return DescriptorStringHolder.buildHolders(this.descriptors);
}
// ********** deployment XML **********
public void exportDeploymentXML() {
File file = this.deploymentXMLFile();
file.getParentFile().mkdirs();
XMLProjectWriter.write(file.getAbsolutePath(), this.buildRuntimeProject());
}
/**
* if the deployment XML file is relative, relate it to the
* project's save directory
*/
public File deploymentXMLFile() {
File file = new File(this.deploymentXMLFileName);
return FileTools.convertToAbsoluteFile(file, this.getSaveDirectory());
}
// ********** export project source **********
public void exportProjectSource() {
File projectSourceFile = this.projectSourceFile();
projectSourceFile.getParentFile().mkdirs();
ProjectClassGenerator generator = new ProjectClassGenerator();
generator.setProject(this.buildRuntimeProject());
generator.setClassName(this.projectSourceClassName);
generator.setPackageName(this.projectSourcePackageName());
generator.setOutputFileName(projectSourceFile.getAbsolutePath());
generator.generate();
}
/**
* combine the current project source directory and Java file name
*/
public File projectSourceFile() {
return new File(this.absoluteProjectSourceDirectory(), Classpath.convertToJavaFileName(this.projectSourceClassName));
}
/**
* if the project source directory is relative, relate it to the
* project's save directory
*/
public File absoluteProjectSourceDirectory() {
File dir = new File(this.projectSourceDirectoryName);
return FileTools.convertToAbsoluteFile(dir, this.getSaveDirectory());
}
public String projectSourcePackageName() {
return ClassTools.packageNameForClassNamed(this.projectSourceClassName);
}
// ********** export model source **********
public File absoluteModelSourceDirectory() {
File dir = new File(this.modelSourceDirectoryName);
return FileTools.convertToAbsoluteFile(dir, this.getSaveDirectory());
}
// ********** automap **********
/**
* Return whether the project is in a state to automap a
* collection of descriptors; in particular, whether the project
* has any tables or XML schemas.
*/
public abstract boolean canAutomapDescriptors();
/**
* Automap the specified descriptors.
*/
public void automap(Collection automapDescriptors) {
// automap inheritance first so the parent descriptors will be set, if possible
for (Iterator stream = automapDescriptors.iterator(); stream.hasNext(); ) {
((MWDescriptor) stream.next()).automapInheritanceHierarchy(automapDescriptors);
}
this.matchClassesAndMetaData(automapDescriptors);
for (Iterator stream = automapDescriptors.iterator(); stream.hasNext(); ) {
MWDescriptor descriptor = (MWDescriptor) stream.next();
descriptor.automap();
}
}
protected void matchClassesAndMetaData(Collection automapDescriptors) {
// do nothing by default
}
/**
* convenience method for subclasses that implement #matchClassesAndMetaData(Collection)
*/
protected DescriptorStringHolder[] buildMetaDataDescriptorStringHolders(Collection automapDescriptors) {
Collection descs = new ArrayList(automapDescriptors.size());
for (Iterator stream = automapDescriptors.iterator(); stream.hasNext(); ) {
MWDescriptor descriptor = (MWDescriptor) stream.next();
if (descriptor.autoMapRequiresMetaData()) {
descs.add(descriptor);
}
}
return DescriptorStringHolder.buildHolders(descs);
}
/**
* convenience constant for subclasses that implement #matchClassesAndMetaData(Collection);
* this is the percentage of classes (or tables/schemas) that must have the same affix (prefix
* or suffix) for that affix to be stripped off before comparison
*/
protected static final float CLASS_META_DATA_PARTIAL_STRING_AFFIX_THRESHOLD = 0.80f; // ???
/**
* convenience constant for subclasses that implement #matchClassesAndMetaData(Collection)
*/
protected static final PartialStringComparatorEngine CLASS_META_DATA_NAME_COMPARATOR_ENGINE =
AffixStrippingPartialStringComparatorEngine.forPrefixStripping(
AffixStrippingPartialStringComparatorEngine.forSuffixStripping(
new ExhaustivePartialStringComparatorEngine(PartialStringComparator.DEFAULT_COMPARATOR),
CLASS_META_DATA_PARTIAL_STRING_AFFIX_THRESHOLD
),
CLASS_META_DATA_PARTIAL_STRING_AFFIX_THRESHOLD
);
// **************** runtime conversion *******************
public Project buildRuntimeProject() {
Project project = new Project();
project.setName(this.getName());
project.setLogin(this.buildRuntimeLogin());
for (Iterator stream = CollectionTools.sortedSet(this.activeDescriptors()).iterator(); stream.hasNext(); ) {
project.addDescriptor(((MWDescriptor) stream.next()).buildRuntimeDescriptor());
}
this.defaultsPolicy.adjustRuntimeProject(project);
return project;
}
protected abstract DatasourceLogin buildRuntimeLogin();
// ********** misc stuff **********
public void nodeRenamed(Node node) {
super.nodeRenamed(node);
if (this.descriptors.contains(node)) {
// if a descriptor has been renamed, we need to fire an "internal"
// change event so the project is marked dirty
this.fireCollectionChanged(DESCRIPTOR_NAMES_COLLECTION);
}
}
/**
* this is used by MWModel - allows us
* to make metaDataRepository() final
*/
public abstract MWModel getMetaDataRepository();
public ProjectSubFileComponentContainer getMetaDataSubComponentContainer() {
return (ProjectSubFileComponentContainer) this.getMetaDataRepository();
}
public Iterator packageNames() {
Set packageNames = new HashSet();
synchronized (this.descriptors) {
for (Iterator stream = this.descriptors.iterator(); stream.hasNext(); ) {
packageNames.add(((MWDescriptor) stream.next()).packageName());
}
}
return packageNames.iterator();
}
public void recalculateAggregatePathsToColumn(MWMappingDescriptor descriptor) {
// by default, do nothing - only used by relational projects for now
}
public void recalculateAggregatePathsToColumn(MWMappingDescriptor descriptor, MWAggregateMapping currentMapping) {
// by default, do nothing - only used by relational projects for now
}
public File saveFile() {
File dir = this.getSaveDirectory();
return (dir == null) ? null : new File(dir, FileTools.FILE_NAME_ENCODER.encode(this.getName()) + FILE_NAME_EXTENSION);
}
public void notifyExpressionsToRecalculateQueryables() {
// by default, do nothing - only used by relational projects for now
}
public void hierarchyChanged(MWClass type) {
for (Iterator stream = this.mappingDescriptors(); stream.hasNext(); ) {
((MWMappingDescriptor) stream.next()).hierarchyChanged(type);
}
}
protected void addTransientAspectNamesTo(Set transientAspectNames) {
super.addTransientAspectNamesTo(transientAspectNames);
transientAspectNames.add(VALIDATING_PROPERTY);
}
protected void addInsignificantAspectNamesTo(Set insignificantAspectNames) {
super.addInsignificantAspectNamesTo(insignificantAspectNames);
insignificantAspectNames.add(VALIDATING_PROPERTY);
}
public void validateBranch() {
this.setIsValidating(true);
super.validateBranch();
this.setIsValidating(false);
}
// ********** SubComponentContainer implementation **********
public Iterator projectSubFileComponents() {
return this.descriptors();
}
public void setProjectSubFileComponents(Collection subComponents) {
this.setDescriptors(subComponents);
}
public Iterator originalProjectSubFileComponentNames() {
return this.descriptorNames.iterator();
}
public void setOriginalProjectSubFileComponentNames(Collection originalSubComponentNames) {
this.descriptorNames = originalSubComponentNames;
}
/**
* return whether the .mwp file has changed and must be written out;
* the .mwp file holds the project itself and all the objects
* beneath it except the "sub-components" written to separate files
* (classes, tables/schemas, and descriptors);
* this is a bit hacky; but it's a nice usability feature
*/
public boolean hasChangedMainProjectSaveFile() {
if (this.isDirty()) {
// the project itself is dirty
return true;
}
for (Iterator stream = this.children(); stream.hasNext(); ) {
if (this.childHasChangedTheSaveFile(stream.next())) {
return true;
}
}
// only the "sub-components" must be dirty
return false;
}
/**
* return whether the specified child of the project is dirty AND
* is written to the .mwp file
*/
private boolean childHasChangedTheSaveFile(Object child) {
if (this.descriptors.contains(child)) {
// descriptors are written to separate files
return false;
}
if (child instanceof ProjectSubFileComponentContainer) {
return ((ProjectSubFileComponentContainer) child).hasChangedMainProjectSaveFile();
}
// the child is NOT a sub-component container,
// so all of its state is written to the .mwp file
return ((Node) child).isDirtyBranch();
}
// ********** TopLink/IOManager methods **********
/**
* the versions are write-only
*/
private String getProductVersionForTopLink() {
return MANIFEST_INTERROGATOR.getVersionNumber();
}
public void setProductVersionForTopLink(String productVersion) {
this.version = productVersion;
}
private String getSchemaVersionForTopLink() {
return CURRENT_SCHEMA_VERSION;
}
private void setSchemaVersionForTopLink(String schemaVersion) {
// ignore - this is for a write-only mapping - the version is hard-coded
}
/**
* convert to platform-independent representation
*/
private String getDeploymentXMLFileNameForTopLink() {
return this.deploymentXMLFileName.replace('\\', '/');
}
/**
* convert to platform-specific representation
*/
private void setDeploymentXMLFileNameForTopLink(String deploymentXMLFileName) {
this.deploymentXMLFileName = new File(deploymentXMLFileName).getPath();
}
/**
* convert to platform-independent representation
*/
private String getProjectSourceDirectoryNameForTopLink() {
return this.projectSourceDirectoryName.replace('\\', '/');
}
/**
* convert to platform-specific representation
*/
private void setProjectSourceDirectoryNameForTopLink(String projectSourceDirectoryName) {
this.projectSourceDirectoryName = new File(projectSourceDirectoryName).getPath();
}
/**
* convert to platform-independent representation
*/
private String getModelSourceDirectoryNameForTopLink() {
return this.modelSourceDirectoryName.replace('\\', '/');
}
/**
* convert to platform-specific representation
*/
private void setModelSourceDirectoryNameForTopLink(String modelSourceDirectoryName) {
this.modelSourceDirectoryName = new File(modelSourceDirectoryName).getPath();
}
/**
* sort the descriptor names for TopLink
*/
private Collection getDescriptorNamesForTopLink() {
List names = new ArrayList(this.descriptors.size());
synchronized (this.descriptors) {
for (Iterator stream = this.descriptors.iterator(); stream.hasNext(); ) {
names.add(((MWDescriptor) stream.next()).getName());
}
}
return CollectionTools.sort(names, Collator.getInstance());
}
/**
* TopLink sets this value, which is then used by the
* ProjectIOManager to read in the actual descriptors
*/
private void setDescriptorNamesForTopLink(Collection descriptorNames) {
this.descriptorNames = descriptorNames;
}
/**
* this is where the whole #postProjectBuild() cascade begins
* @see ProjectReader#readProject()
*/
public void postProjectBuild() {
// set the child backpointers first
this.setChildBackpointers();
// resolve all handles and such
this.resolveInternalReferences();
// then, cascade to the entire project
super.postProjectBuild();
this.buildBasicTypes();
// now, mark the entire project as "clean"
if (this.version != null) {
// do NOT mark project clean if it is a legacy project
this.markEntireBranchClean();
}
}
protected void resolveInternalReferences() {
// resolve handles
this.resolveHandles();
}
/** this is where all references to root objects are resolved */
private void resolveHandles() {
this.resolveClassHandles();
this.resolveMetadataHandles();
this.resolveColumnHandles();
this.resolveReferenceHandles();
this.resolveDescriptorHandles();
this.resolveMethodHandles();
}
/**
* this is called just after the project is read in,
* so we don't need to mark the project dirty
*/
private void setSaveDirectoryForIOManager(File saveDirectory) {
this.saveDirectory = saveDirectory;
}
/**
* this is called just after the project is read in,
* so we don't need to mark the project dirty
*/
private void setSPIManagerForIOManager(SPIManager spiManager) {
this.spiManager = spiManager;
}
// ********** displaying and printing **********
public String displayString() {
return this.getName();
}
public void toString(StringBuffer sb) {
sb.append(getName());
}
// ********** member classes **********
private static class LocalManifestDefaults implements Defaults {
public String defaultSpecificationTitle() {
return "EclipseLink";
}
public String defaultSpecificationVendor() {
return "Eclipse";
}
public String defaultReleaseDesignation() {
return "Version 1.0.0";
}
public String defaultLibraryDesignation() {
return "Workbench";
}
public String defaultSpecificationVersion() {
return "1.0.0";
}
public String defaultImplementationVersion() {
return this.defaultSpecificationVersion();
}
}
// ***************** weaving *****************
public boolean usesWeaving() {
return this.usesWeaving;
}
public void setUsesWeaving(boolean newValue) {
boolean oldValue = this.usesWeaving;
this.usesWeaving = newValue;
firePropertyChanged(USES_WEAVING_PROPERTY, oldValue, newValue);
}
}