blob: c84801e4cb2905f73b09c11ac072f5805a53d36b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2011 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.jpa.core.internal.jpa1.context.orm;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Vector;
import org.eclipse.jpt.common.core.utility.TextRange;
import org.eclipse.jpt.common.utility.internal.CollectionTools;
import org.eclipse.jpt.common.utility.internal.iterables.CompositeListIterable;
import org.eclipse.jpt.common.utility.internal.iterables.EmptyIterable;
import org.eclipse.jpt.common.utility.internal.iterables.ListIterable;
import org.eclipse.jpt.common.utility.internal.iterables.LiveCloneListIterable;
import org.eclipse.jpt.common.utility.internal.iterators.EmptyIterator;
import org.eclipse.jpt.common.utility.internal.iterators.FilteringIterator;
import org.eclipse.jpt.jpa.core.context.BaseColumn;
import org.eclipse.jpt.jpa.core.context.Override_;
import org.eclipse.jpt.jpa.core.context.TypeMapping;
import org.eclipse.jpt.jpa.core.context.VirtualOverride;
import org.eclipse.jpt.jpa.core.context.XmlContextNode;
import org.eclipse.jpt.jpa.core.context.orm.OrmOverride;
import org.eclipse.jpt.jpa.core.context.orm.OrmOverrideContainer;
import org.eclipse.jpt.jpa.core.context.orm.OrmReadOnlyOverride;
import org.eclipse.jpt.jpa.core.context.orm.OrmVirtualOverride;
import org.eclipse.jpt.jpa.core.internal.context.BaseColumnTextRangeResolver;
import org.eclipse.jpt.jpa.core.internal.context.ContextContainerTools;
import org.eclipse.jpt.jpa.core.internal.context.JptValidator;
import org.eclipse.jpt.jpa.core.internal.context.OverrideTextRangeResolver;
import org.eclipse.jpt.jpa.core.internal.context.orm.AbstractOrmXmlContextNode;
import org.eclipse.jpt.jpa.core.resource.orm.XmlOverride;
import org.eclipse.wst.validation.internal.provisional.core.IMessage;
import org.eclipse.wst.validation.internal.provisional.core.IReporter;
/**
* <code>orm.xml</code> override container
*/
public abstract class AbstractOrmOverrideContainer<
O extends OrmOverrideContainer.Owner,
R extends OrmReadOnlyOverride,
S extends OrmOverride,
V extends OrmVirtualOverride,
X extends XmlOverride
>
extends AbstractOrmXmlContextNode
implements OrmOverrideContainer
{
// this can be null if the container is "read-only" (i.e. a "null" container)
protected final O owner;
protected final Vector<S> specifiedOverrides = new Vector<S>();
protected final SpecifiedOverrideContainerAdapter specifiedOverrideContainerAdapter = new SpecifiedOverrideContainerAdapter();
protected final Vector<V> virtualOverrides = new Vector<V>();
protected final VirtualOverrideContainerAdapter virtualOverrideContainerAdapter = new VirtualOverrideContainerAdapter();
protected AbstractOrmOverrideContainer(XmlContextNode parent, O owner) {
super(parent);
this.owner = owner;
this.initializeSpecifiedOverrides();
}
// ********** synchronize/update **********
@Override
public void synchronizeWithResourceModel() {
super.synchronizeWithResourceModel();
this.syncSpecifiedOverrides();
// the virtual overrides do not need a sync
}
@Override
public void update() {
super.update();
this.updateNodes(this.getSpecifiedOverrides());
this.updateVirtualOverrides();
}
// ********** overrides **********
public ListIterator<R> overrides() {
return this.getOverrides().iterator();
}
@SuppressWarnings("unchecked")
protected ListIterable<R> getOverrides() {
return new CompositeListIterable<R>(this.getReadOnlySpecifiedOverrides(), this.getReadOnlyVirtualOverrides());
}
public int overridesSize() {
return this.specifiedOverrides.size() + this.virtualOverrides.size();
}
public R getOverrideNamed(String name) {
return this.selectOverrideNamed(this.getOverrides(), name);
}
// ********** override conversions **********
/**
* <em>Silently</em> add the new virtual override before removing the
* specified override, or the <em>update</em> will discover the missing
* virtual override and add it preemptively.
*/
public V convertOverrideToVirtual(Override_ override) {
if (override.isVirtual()) {
throw new IllegalArgumentException("Override is already virtual: " + override); //$NON-NLS-1$
}
@SuppressWarnings("unchecked")
S specifiedOverride = (S) override;
int virtualIndex = this.virtualOverrides.size();
String overrideName = specifiedOverride.getName();
V virtualOverride = null;
// make sure the specified override actually overrides something before building the virtual override
if (this.overrideWillBeVirtual(overrideName, specifiedOverride)) {
virtualOverride = this.buildVirtualOverride(overrideName);
this.virtualOverrides.add(virtualIndex, virtualOverride);
}
this.removeSpecifiedOverride(specifiedOverride); // trigger update
if (virtualOverride != null) {
this.fireItemAdded(VIRTUAL_OVERRIDES_LIST, virtualIndex, virtualOverride);
}
return virtualOverride;
}
/**
* Return whether the specified override name will be a
* <em>virtual</em> override when the specified specified override is
* removed from the container. The override name must be among the
* valid override names and it must not correspond to any of the
* remaining specified overrides.
*/
protected boolean overrideWillBeVirtual(String overrideName, S specifiedOverrideToBeRemoved) {
return CollectionTools.contains(this.allOverridableNames(), overrideName) &&
(this.getSpecifiedOverrideNamed(overrideName, specifiedOverrideToBeRemoved) == null);
}
/**
* <em>Silently</em> remove the virtual override and add the new specified
* override before naming the specified override, or the <em>update</em>
* will discover the dangling virtual override and remove it preemptively.
*/
public S convertOverrideToSpecified(VirtualOverride override) {
if ( ! override.isVirtual()) {
throw new IllegalArgumentException("Override is already specified: " + override); //$NON-NLS-1$
}
@SuppressWarnings("unchecked")
V virtualOverride = (V) override;
int virtualIndex = this.virtualOverrides.indexOf(virtualOverride);
this.virtualOverrides.remove(virtualIndex);
int specifiedIndex = this.specifiedOverrides.size();
X xmlOverride = this.buildXmlOverride();
S specifiedOverride = this.buildSpecifiedOverride(xmlOverride);
this.specifiedOverrides.add(specifiedIndex, specifiedOverride);
this.owner.getXmlOverrides().add(specifiedIndex, xmlOverride);
this.initializeSpecifiedOverride(specifiedOverride, virtualOverride); // trigger update
this.fireItemRemoved(VIRTUAL_OVERRIDES_LIST, virtualIndex, virtualOverride);
this.fireItemAdded(SPECIFIED_OVERRIDES_LIST, specifiedIndex, specifiedOverride);
return specifiedOverride;
}
protected abstract void initializeSpecifiedOverride(S specifiedOverride, V virtualOverride);
// ********** specified overrides **********
public ListIterator<S> specifiedOverrides() {
return this.getSpecifiedOverrides().iterator();
}
protected ListIterable<S> getSpecifiedOverrides() {
return new LiveCloneListIterable<S>(this.specifiedOverrides);
}
@SuppressWarnings("unchecked")
protected ListIterable<R> getReadOnlySpecifiedOverrides() {
// S should always be a subtype of R, but we can't enforce that in the
// class declaration...
return (ListIterable<R>) this.getSpecifiedOverrides();
}
public int specifiedOverridesSize() {
return this.specifiedOverrides.size();
}
public S getSpecifiedOverride(int index) {
return this.specifiedOverrides.get(index);
}
public S getSpecifiedOverrideNamed(String name) {
return this.getSpecifiedOverrideNamed(name, null);
}
@SuppressWarnings("unchecked")
protected S getSpecifiedOverrideNamed(String name, S exclude) {
return (S) this.selectOverrideNamed(this.getReadOnlySpecifiedOverrides(), name, exclude);
}
protected S addSpecifiedOverride() {
return this.addSpecifiedOverride(this.specifiedOverrides.size());
}
protected S addSpecifiedOverride(int index) {
X xmlOverride = this.buildXmlOverride();
S override = this.addSpecifiedOverride_(index, xmlOverride);
this.owner.getXmlOverrides().add(index, xmlOverride);
return override;
}
protected abstract X buildXmlOverride();
protected void removeSpecifiedOverride(S override) {
this.removeSpecifiedOverride(this.specifiedOverrides.indexOf(override));
}
protected void removeSpecifiedOverride(int index) {
this.removeSpecifiedOverride_(index);
this.owner.getXmlOverrides().remove(index);
}
protected void removeSpecifiedOverride_(int index) {
this.removeItemFromList(index, this.specifiedOverrides, SPECIFIED_OVERRIDES_LIST);
}
public void moveSpecifiedOverride(int targetIndex, int sourceIndex) {
this.moveItemInList(targetIndex, sourceIndex, this.specifiedOverrides, SPECIFIED_OVERRIDES_LIST);
this.owner.getXmlOverrides().move(targetIndex, sourceIndex);
}
protected void initializeSpecifiedOverrides() {
for (X xmlOverride : this.getXmlOverrides()) {
this.specifiedOverrides.add(this.buildSpecifiedOverride(xmlOverride));
}
}
protected abstract S buildSpecifiedOverride(X xmlOverride);
protected void syncSpecifiedOverrides() {
ContextContainerTools.synchronizeWithResourceModel(this.specifiedOverrideContainerAdapter);
}
protected Iterable<X> getXmlOverrides() {
return (this.owner == null) ? EmptyIterable.<X>instance() : this.getXmlOverrides_();
}
/**
* pre-condition: {@link #owner} is not <code>null</code>
*/
protected abstract Iterable<X> getXmlOverrides_();
protected void moveSpecifiedOverride_(int index, S override) {
this.moveItemInList(index, override, this.specifiedOverrides, SPECIFIED_OVERRIDES_LIST);
}
protected S addSpecifiedOverride_(int index, X xmlOverride) {
S override = this.buildSpecifiedOverride(xmlOverride);
this.addItemToList(index, override, this.specifiedOverrides, SPECIFIED_OVERRIDES_LIST);
return override;
}
protected void removeSpecifiedOverride_(S override) {
this.removeSpecifiedOverride_(this.specifiedOverrides.indexOf(override));
}
/**
* specified override container adapter
*/
protected class SpecifiedOverrideContainerAdapter
implements ContextContainerTools.Adapter<S, X>
{
public Iterable<S> getContextElements() {
return AbstractOrmOverrideContainer.this.getSpecifiedOverrides();
}
public Iterable<X> getResourceElements() {
return AbstractOrmOverrideContainer.this.getXmlOverrides();
}
@SuppressWarnings("unchecked")
public X getResourceElement(S contextElement) {
return (X) contextElement.getXmlOverride();
}
public void moveContextElement(int index, S element) {
AbstractOrmOverrideContainer.this.moveSpecifiedOverride_(index, element);
}
public void addContextElement(int index, X resourceElement) {
AbstractOrmOverrideContainer.this.addSpecifiedOverride_(index, resourceElement);
}
public void removeContextElement(S element) {
AbstractOrmOverrideContainer.this.removeSpecifiedOverride_(element);
}
}
// ********** virtual overrides **********
public ListIterator<V> virtualOverrides() {
return this.getVirtualOverrides().iterator();
}
protected ListIterable<V> getVirtualOverrides() {
return new LiveCloneListIterable<V>(this.virtualOverrides);
}
@SuppressWarnings("unchecked")
protected ListIterable<R> getReadOnlyVirtualOverrides() {
// V should always be a subtype of R, but we can't enforce that in the
// class declaration...
return (ListIterable<R>) this.getVirtualOverrides();
}
public int virtualOverridesSize() {
return this.virtualOverrides.size();
}
protected void updateVirtualOverrides() {
ContextContainerTools.update(this.virtualOverrideContainerAdapter);
}
/**
* Return the overridable names that are not already in the list of
* specified overrides.
*/
protected Iterable<String> getVirtualOverrideNames() {
return CollectionTools.iterable(this.virtualOverrideNames());
}
protected Iterator<String> virtualOverrideNames() {
return new FilteringIterator<String>(this.allOverridableNames()) {
@Override
protected boolean accept(String name) {
return AbstractOrmOverrideContainer.this.overrideIsVirtual(name);
}
};
}
protected boolean overrideIsVirtual(String name) {
return this.getSpecifiedOverrideNamed(name) == null;
}
protected void moveVirtualOverride(int index, V override) {
this.moveItemInList(index, override, this.virtualOverrides, VIRTUAL_OVERRIDES_LIST);
}
protected V addVirtualOverride(int index, String name) {
V override = this.buildVirtualOverride(name);
this.addItemToList(index, override, this.virtualOverrides, VIRTUAL_OVERRIDES_LIST);
return override;
}
protected abstract V buildVirtualOverride(String name);
protected void removeVirtualOverride(V override) {
this.removeItemFromList(override, this.virtualOverrides, VIRTUAL_OVERRIDES_LIST);
}
/**
* virtual override container adapter
* NB: the context override is matched with a resource override by name
*/
protected class VirtualOverrideContainerAdapter
implements ContextContainerTools.Adapter<V, String>
{
public Iterable<V> getContextElements() {
return AbstractOrmOverrideContainer.this.getVirtualOverrides();
}
public Iterable<String> getResourceElements() {
return AbstractOrmOverrideContainer.this.getVirtualOverrideNames();
}
public String getResourceElement(V contextElement) {
return contextElement.getName();
}
public void moveContextElement(int index, V element) {
AbstractOrmOverrideContainer.this.moveVirtualOverride(index, element);
}
public void addContextElement(int index, String resourceElement) {
AbstractOrmOverrideContainer.this.addVirtualOverride(index, resourceElement);
}
public void removeContextElement(V element) {
AbstractOrmOverrideContainer.this.removeVirtualOverride(element);
}
}
// ********** misc **********
@Override
public XmlContextNode getParent() {
return (XmlContextNode) super.getParent();
}
public TypeMapping getOverridableTypeMapping() {
return this.owner.getOverridableTypeMapping();
}
public TypeMapping getTypeMapping() {
return this.owner.getTypeMapping();
}
public Iterator<String> allOverridableNames() {
return (this.owner != null) ? this.owner.allOverridableNames() : EmptyIterator.<String>instance();
}
public boolean tableNameIsInvalid(String tableName) {
return this.owner.tableNameIsInvalid(tableName);
}
public Iterator<String> candidateTableNames() {
return this.owner.candidateTableNames();
}
public org.eclipse.jpt.jpa.db.Table resolveDbTable(String tableName) {
return this.owner.resolveDbTable(tableName);
}
public String getDefaultTableName() {
return this.owner.getDefaultTableName();
}
public JptValidator buildValidator(Override_ override, OverrideTextRangeResolver textRangeResolver) {
return this.owner.buildValidator(override, this, textRangeResolver);
}
public JptValidator buildColumnValidator(Override_ override, BaseColumn column, BaseColumn.Owner columnOwner, BaseColumnTextRangeResolver textRangeResolver) {
return this.owner.buildColumnValidator(override, column, columnOwner, textRangeResolver);
}
protected R selectOverrideNamed(Iterable<R> overrides, String name) {
return this.selectOverrideNamed(overrides, name, null);
}
protected R selectOverrideNamed(Iterable<R> overrides, String name, S exclude) {
for (R override : overrides) {
if (override == exclude) {
continue; // skip
}
if (this.valuesAreEqual(override.getName(), name)) {
return override;
}
}
return null;
}
// ********** validation **********
@Override
public void validate(List<IMessage> messages, IReporter reporter) {
super.validate(messages, reporter);
for (R override : this.getOverrides()) {
override.validate(messages, reporter);
}
}
public TextRange getValidationTextRange() {
return (this.specifiedOverrides.size() > 0) ?
this.specifiedOverrides.get(0).getValidationTextRange() :
this.owner.getValidationTextRange();
}
}