/*******************************************************************************
* 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.schema;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.persistence.tools.workbench.mappingsmodel.MWModel;
import org.eclipse.persistence.tools.workbench.mappingsmodel.ProjectSubFileComponentContainer;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.project.xml.MWXmlProject;
import org.eclipse.persistence.tools.workbench.mappingsmodel.resource.ResourceException;
import org.eclipse.persistence.tools.workbench.utility.CollectionTools;
import org.eclipse.persistence.tools.workbench.utility.node.Node;
import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.oxm.mappings.XMLCompositeDirectCollectionMapping;
public final class MWXmlSchemaRepository
extends MWModel
implements ProjectSubFileComponentContainer
{
// queried reflectively by I/O Manager
private static final String SUB_DIRECTORY_NAME = "schemas";
// **************** Instance Variables ************************************
/** keyed by schema name */
private Map schemas;
public final static String SCHEMAS_COLLECTION = "schemas";
/**
* these schema 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 schemaNames;
private final static String SCHEMA_NAMES_COLLECTION = "schemaNames";
// **************** Constructors ******************************************
private MWXmlSchemaRepository() {
super();
}
public MWXmlSchemaRepository(MWXmlProject parent) {
super(parent);
}
// **************** Initialization ****************************************
protected /*private-protected*/ void initialize(Node parent) {
super.initialize(parent);
this.schemas = new Hashtable();
this.schemaNames = new HashSet();
}
protected /*private-protected*/ void addChildrenTo(List children) {
super.addChildrenTo(children);
synchronized (this.schemas) { children.addAll(this.schemas.values()); }
}
// **************** Schema Management *************************************
public Iterator schemas() {
return this.schemas.values().iterator();
}
public int schemasSize() {
return this.schemas.size();
}
public MWXmlSchema getSchema(String schemaName) {
return (MWXmlSchema) this.schemas.get(schemaName);
}
public MWXmlSchema createSchemaFromFile(String name, String file)
throws ResourceException
{
return this.addSchema(MWXmlSchema.createFromFile(this, name, file));
}
public MWXmlSchema createSchemaFromUrl(String name, String url)
throws ResourceException
{
return this.addSchema(MWXmlSchema.createFromUrl(this, name, url));
}
public MWXmlSchema createSchemaFromClasspath(String name, String resource)
throws ResourceException
{
return this.addSchema(MWXmlSchema.createFromClasspath(this, name, resource));
}
private MWXmlSchema addSchema(MWXmlSchema schema)
throws ResourceException
{
schema.reload();
this.checkSchemaName(schema.getName(), schema);
this.schemas.put(schema.getName(), schema);
this.fireItemAdded(SCHEMAS_COLLECTION, schema);
return schema;
}
private void checkSchemaName(String schemaName, MWXmlSchema schema) {
if (this.getSchema(schemaName) != null) {
throw new IllegalArgumentException("Schema with name already exists.");
}
MWXmlSchema matchIgnoreCase = this.getSchemaIgnoreCase(schemaName);
if ((matchIgnoreCase != null) && (matchIgnoreCase != schema)) {
throw new IllegalArgumentException("Schema with name, but different case, already exists: \"" + matchIgnoreCase.getName() + "\".");
}
}
public boolean containsSchemaIgnoreCase(String name) {
return this.getSchemaIgnoreCase(name) != null;
}
public MWXmlSchema getSchemaIgnoreCase(String name) {
for (Iterator stream = this.schemas.values().iterator(); stream.hasNext(); ) {
MWXmlSchema schema = (MWXmlSchema) stream.next();
if (schema.getName().equalsIgnoreCase(name)) {
return schema;
}
}
return null;
}
public void removeSchema(MWXmlSchema schema) {
if (this.getSchema(schema.getName()) == null)
throw new IllegalArgumentException("Schema with name does not exist.");
this.schemas.remove(schema.getName());
this.fireItemRemoved(SCHEMAS_COLLECTION, schema);
getProject().nodeRemoved(schema);
}
void schemaRenamed(String oldName, String newName, MWXmlSchema schema) {
synchronized (this.schemas) {
this.checkSchemaName(newName, schema);
this.schemas.put(newName, this.schemas.remove(oldName));
}
// if a schema has been renamed, we need to fire an "internal"
// change event so the repository is marked dirty
this.fireCollectionChanged(SCHEMA_NAMES_COLLECTION);
}
/**
* performance tuning: override this method and assume
* the repository's descendants have NO references (handles)
* to any models other than other descendants of the repository
*/
public void nodeRemoved(Node node) {
if (node.isDescendantOf(this)) {
super.nodeRemoved(node);
}
}
/**
* performance tuning: override this method and assume
* the repository's descendants have NO references (handles)
* to any models other than other descendants of the repository
*/
public void nodeRenamed(Node node) {
if (node.isDescendantOf(this)) {
super.nodeRenamed(node);
// we handle a renamed schema directly in #schemaRenamed(String, String)
}
}
/**
* performance tuning: ignore this method - assume there are no
* references to mappings in the schema repository or its descendants
*/
public void mappingReplaced(MWMapping oldMapping, MWMapping newMapping) {
// do nothing
}
/**
* performance tuning: ignore this method - assume there are no
* references to descriptors in the database or its descendants
*/
public void descriptorReplaced(MWDescriptor oldDescriptor, MWDescriptor newDescriptor) {
// do nothing
}
/**
* performance tuning: ignore this method - assume there are no
* references to mappings in the schema repository or its descendants
*/
public void descriptorUnmapped(Collection mappings) {
// do nothing
}
// ********** SubComponentContainer implementation **********
public Iterator projectSubFileComponents() {
return this.schemas();
}
public void setProjectSubFileComponents(Collection subComponents) {
this.schemas = new Hashtable(subComponents.size());
for (Iterator stream = subComponents.iterator(); stream.hasNext(); ) {
MWXmlSchema schema = (MWXmlSchema) stream.next();
this.schemas.put(schema.getName(), schema);
}
}
public Iterator originalProjectSubFileComponentNames() {
return this.schemaNames.iterator();
}
public void setOriginalProjectSubFileComponentNames(Collection originalSubComponentNames) {
this.schemaNames = originalSubComponentNames;
}
public boolean hasChangedMainProjectSaveFile() {
if (this.isDirty()) {
// the repository itself is dirty
return true;
}
for (Iterator stream = this.children(); stream.hasNext(); ) {
if (this.childHasChangedTheProjectSaveFile(stream.next())) {
return true;
}
}
// the schemas might be dirty
return false;
}
/**
* return whether the specified child of the repository is dirty AND
* is written to the .mwp file
*/
private boolean childHasChangedTheProjectSaveFile(Object child) {
if (this.schemas.containsValue(child)) {
// tables are written to separate files
return false;
}
// the child is NOT a table,
// so all of its state is written to the .mwp file
return ((Node) child).isDirtyBranch();
}
//******************** TopLink methods ************************
public static XMLDescriptor buildDescriptor(){
XMLDescriptor descriptor = new XMLDescriptor();
descriptor.setJavaClass(MWXmlSchemaRepository.class);
XMLCompositeDirectCollectionMapping schemaNamesMapping = new XMLCompositeDirectCollectionMapping();
schemaNamesMapping.setAttributeName("schemaNames");
schemaNamesMapping.setSetMethodName("setSchemaNamesForTopLink");
schemaNamesMapping.setGetMethodName("getSchemaNamesForTopLink");
schemaNamesMapping.useCollectionClass(HashSet.class);
schemaNamesMapping.setXPath("schema-names/name/text()");
descriptor.addMapping(schemaNamesMapping);
return descriptor;
}
/**
* sort the schema names for TopLink
*/
private Collection getSchemaNamesForTopLink() {
List names = new ArrayList(this.schemas.size());
synchronized (this.schemas) {
for (Iterator stream = this.schemas.keySet().iterator(); stream.hasNext(); ) {
names.add(stream.next());
}
}
return CollectionTools.sort(names, Collator.getInstance());
}
/**
* TopLink sets this value, which is then used by the
* ProjectIOManager to read in the actual schemata
*/
private void setSchemaNamesForTopLink(Collection schemaNames) {
this.schemaNames = schemaNames;
}
}