| /******************************************************************************* |
| * Copyright (c) 2007, 2016 Oracle. All rights reserved. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License 2.0, which accompanies this distribution |
| * and is available at https://www.eclipse.org/legal/epl-2.0/. |
| * |
| * Contributors: |
| * Oracle - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.jpt.jpa.core.internal.context; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Vector; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.jpt.common.core.JptResourceType; |
| import org.eclipse.jpt.common.core.internal.utility.ValidationMessageTools; |
| import org.eclipse.jpt.common.core.utility.TextRange; |
| import org.eclipse.jpt.common.core.utility.ValidationMessage; |
| import org.eclipse.jpt.common.utility.internal.ObjectTools; |
| import org.eclipse.jpt.common.utility.internal.collection.CollectionTools; |
| import org.eclipse.jpt.common.utility.internal.iterator.IteratorTools; |
| import org.eclipse.jpt.common.utility.iterable.ListIterable; |
| import org.eclipse.jpt.jpa.core.JpaModel; |
| import org.eclipse.jpt.jpa.core.context.JpaContextModel; |
| import org.eclipse.jpt.jpa.core.context.MappingFile; |
| import org.eclipse.jpt.jpa.core.context.persistence.PersistenceUnit; |
| import org.eclipse.jpt.jpa.core.internal.AbstractJpaModel; |
| import org.eclipse.jpt.jpa.db.Catalog; |
| import org.eclipse.jpt.jpa.db.Schema; |
| import org.eclipse.jpt.jpa.db.SchemaContainer; |
| import org.eclipse.jst.j2ee.model.internal.validation.ValidationCancelledException; |
| import org.eclipse.wst.validation.internal.provisional.core.IMessage; |
| import org.eclipse.wst.validation.internal.provisional.core.IReporter; |
| |
| public abstract class AbstractJpaContextModel<P extends JpaContextModel> |
| extends AbstractJpaModel<P> |
| implements JpaContextModel |
| { |
| protected AbstractJpaContextModel(P parent) { |
| super(parent); |
| } |
| |
| |
| // ********** synchronize/update ********** |
| |
| public void synchronizeWithResourceModel(IProgressMonitor monitor) { |
| if (monitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| } |
| |
| /** |
| * convenience method |
| */ |
| protected void synchronizeModelsWithResourceModel(Iterable<? extends JpaContextModel> models, IProgressMonitor monitor) { |
| for (JpaContextModel model : models) { |
| model.synchronizeWithResourceModel(monitor); |
| } |
| } |
| |
| public void update(IProgressMonitor monitor) { |
| if (monitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| } |
| |
| /** |
| * convenience method |
| */ |
| protected void updateModels(Iterable<? extends JpaContextModel> models, IProgressMonitor monitor) { |
| for (JpaContextModel model : models) { |
| model.update(monitor); |
| } |
| } |
| |
| |
| // ********** containment hierarchy ********** |
| |
| /** |
| * Overridden in:<ul> |
| * <li>{@link org.eclipse.jpt.jpa.core.internal.context.java.AbstractJavaContextModel#getResourceType() AbstractJavaJpaContextModel} |
| * <li>{@link org.eclipse.jpt.jpa.core.internal.jpa1.context.java.GenericJarFile#getResourceType() GenericJarFile} |
| * <li>{@link org.eclipse.jpt.jpa.core.internal.jpa1.context.orm.GenericOrmXml#getResourceType() GenericOrmXml} |
| * <li>{@link org.eclipse.jpt.jpa.core.internal.jpa1.context.persistence.GenericPersistenceXml#getResourceType() GenericPersistenceXml} |
| * </ul> |
| */ |
| public JptResourceType getResourceType() { |
| return this.parent.getResourceType(); |
| } |
| |
| /** |
| * Overridden in:<ul> |
| * <li>{@link org.eclipse.jpt.jpa.core.internal.context.persistence.AbstractPersistenceUnit#getPersistenceUnit() AbstractPersistenceUnit} |
| * to return itself |
| * <li>{@link org.eclipse.jpt.jpa.core.internal.jpa1.context.GenericContextRoot#getPersistenceUnit() GenericContextModelRoot} |
| * to return <code>null</code> |
| * </ul> |
| */ |
| public PersistenceUnit getPersistenceUnit() { |
| return this.parent.getPersistenceUnit(); |
| } |
| |
| /** |
| * Overridden in:<ul> |
| * <li>{@link org.eclipse.jpt.jpa.core.internal.context.orm.AbstractEntityMappings#getMappingFileRoot() AbstractEntityMappings} |
| * to return itself |
| * <li>{@link org.eclipse.jpt.jpa.core.internal.jpa1.context.GenericContextRoot#getMappingFileRoot() GenericContextModelRoot} |
| * to return <code>null</code> |
| * </ul> |
| */ |
| public MappingFile.Root getMappingFileRoot() { |
| return this.parent.getMappingFileRoot(); |
| } |
| |
| |
| // ********** validation ********** |
| |
| /** |
| * All subclass implementations should be |
| * preceded by a "super" call to this method. |
| */ |
| public void validate(List<IMessage> messages, IReporter reporter) { |
| if (reporter.isCancelled()) { |
| throw new ValidationCancelledException(); |
| } |
| } |
| |
| /** |
| * Return the specified text range if it is not <code>null</code>; if it is |
| * <code>null</code>, return the model's validation text range. |
| * Typically, the specified text range is for one of the model's children. |
| */ |
| protected TextRange getValidationTextRange(TextRange textRange) { |
| return (textRange != null) ? textRange : this.getValidationTextRange(); |
| } |
| |
| /** |
| * Validate the specified model if it is not <code>null</code>. |
| */ |
| protected void validateModel(JpaContextModel model, List<IMessage> messages, IReporter reporter) { |
| if (model != null) { |
| model.validate(messages, reporter); |
| } |
| } |
| |
| /** |
| * Validate the specified models. |
| */ |
| protected void validateModels(Iterable<? extends JpaContextModel> models, List<IMessage> messages, IReporter reporter) { |
| for (JpaContextModel model : models) { |
| model.validate(messages, reporter); |
| } |
| } |
| |
| /** |
| * @see #buildValidationMessage(JpaModel, ValidationMessage) |
| * @see IMessage#HIGH_SEVERITY |
| */ |
| protected IMessage buildValidationMessage(ValidationMessage message) { |
| return this.buildValidationMessage(this, message); |
| } |
| |
| /** |
| * @see ValidationMessageTools#buildValidationMessage(IResource, ValidationMessage) |
| * @see IMessage#HIGH_SEVERITY |
| */ |
| protected IMessage buildValidationMessage(JpaModel target, ValidationMessage message) { |
| return ValidationMessageTools.buildValidationMessage(target.getResource(), message); |
| } |
| |
| /** |
| * @see #buildValidationMessage(JpaModel, ValidationMessage, Object[]) |
| * @see IMessage#HIGH_SEVERITY |
| */ |
| protected IMessage buildValidationMessage(ValidationMessage message, Object... args) { |
| return this.buildValidationMessage(this, message, args); |
| } |
| |
| /** |
| * @see ValidationMessageTools#buildValidationMessage(IResource, ValidationMessage, Object[]) |
| * @see IMessage#HIGH_SEVERITY |
| */ |
| protected IMessage buildValidationMessage(JpaModel target, ValidationMessage message, Object... args) { |
| return ValidationMessageTools.buildValidationMessage(target.getResource(), message, args); |
| } |
| |
| /** |
| * @see #buildValidationMessage(JpaModel, TextRange, ValidationMessage) |
| * @see IMessage#HIGH_SEVERITY |
| */ |
| protected IMessage buildValidationMessage(TextRange textRange, ValidationMessage message) { |
| return this.buildValidationMessage(this, textRange, message); |
| } |
| |
| /** |
| * @see ValidationMessageTools#buildValidationMessage(IResource, TextRange, ValidationMessage) |
| * @see IMessage#HIGH_SEVERITY |
| */ |
| protected IMessage buildValidationMessage(JpaModel target, TextRange textRange, ValidationMessage message) { |
| return ValidationMessageTools.buildValidationMessage(target.getResource(), textRange, message); |
| } |
| |
| /** |
| * @see #buildValidationMessage(JpaModel, TextRange, ValidationMessage, Object[]) |
| * @see IMessage#HIGH_SEVERITY |
| */ |
| protected IMessage buildValidationMessage(TextRange textRange, ValidationMessage message, Object... args) { |
| return this.buildValidationMessage(this, textRange, message, args); |
| } |
| |
| /** |
| * @see ValidationMessageTools#buildValidationMessage(IResource, TextRange, ValidationMessage, Object[]) |
| * @see IMessage#HIGH_SEVERITY |
| */ |
| protected IMessage buildValidationMessage(JpaModel target, TextRange textRange, ValidationMessage message, Object... args) { |
| return ValidationMessageTools.buildValidationMessage(target.getResource(), textRange, message, args); |
| } |
| |
| |
| // *********** completion proposals *********** |
| |
| public Iterable<String> getCompletionProposals(int pos) { |
| if (this.connectionProfileIsActive()) { |
| Iterable<String> result = this.getConnectedCompletionProposals(pos); |
| if (result != null) { |
| return result; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * This method is called if the database is connected, allowing us to |
| * get candidates from the various database tables etc. |
| * This method should <em>not</em> be cascaded to "child" objects; it should |
| * only return candidates for the current object. The cascading is |
| * handled by {@link #getCompletionProposals(int)}. |
| */ |
| @SuppressWarnings("unused") |
| protected Iterable<String> getConnectedCompletionProposals(int pos) { |
| return null; |
| } |
| |
| |
| // ********** database stuff ********** |
| |
| public Schema getContextDefaultDbSchema() { |
| SchemaContainer dbSchemaContainer = this.getContextDefaultDbSchemaContainer(); |
| return (dbSchemaContainer == null) ? null : dbSchemaContainer.getSchemaForIdentifier(this.getContextDefaultSchema()); |
| } |
| |
| protected String getContextDefaultSchema() { |
| MappingFile.Root mfr = this.getMappingFileRoot(); |
| return (mfr != null) ? mfr.getSchema() : this.getPersistenceUnit().getDefaultSchema(); |
| } |
| |
| /** |
| * If we don't have a catalog (i.e. we don't even have a <em>default</em> catalog), |
| * then the database probably does not support catalogs; and we need to |
| * get the schema directly from the database. |
| */ |
| public SchemaContainer getContextDefaultDbSchemaContainer() { |
| String catalog = this.getContextDefaultCatalog(); |
| return (catalog != null) ? this.resolveDbCatalog(catalog) : this.getDatabase(); |
| } |
| |
| /** |
| * If we don't have a catalog (i.e. we don't even have a <em>default</em> |
| * catalog), then the database probably does not support catalogs. |
| */ |
| public Catalog getContextDefaultDbCatalog() { |
| String catalog = this.getContextDefaultCatalog(); |
| return (catalog == null) ? null : this.resolveDbCatalog(catalog); |
| } |
| |
| protected String getContextDefaultCatalog() { |
| MappingFile.Root mfr = this.getMappingFileRoot(); |
| return (mfr != null) ? mfr.getCatalog() : this.getPersistenceUnit().getDefaultCatalog(); |
| } |
| |
| |
| // ********** containers ********** |
| |
| /** |
| * Adapter used to synchronize a collection of context models with the |
| * corresponding collection of resource models. |
| * @param <C> the type of context elements |
| * @param <R> the type of resource elements |
| */ |
| public static abstract class Container<C, R> |
| implements ListIterable<C> |
| { |
| /** |
| * "Context" elements. |
| */ |
| protected final Vector<C> elements = new Vector<>(); |
| |
| /** |
| * Aspect name used for event notification when the |
| * container's contents change. |
| */ |
| protected final String aspectName; |
| |
| protected final Adapter<C, R> adapter; |
| |
| |
| protected Container(String aspectName, Adapter<C, R> adapter) { |
| super(); |
| this.aspectName = aspectName; |
| this.adapter = adapter; |
| } |
| |
| /** |
| * Subclasses call this as necessary. |
| */ |
| protected void initialize() { |
| for (R resourceElement : this.adapter.getResourceElements()) { |
| this.elements.add(this.adapter.buildContextElement(resourceElement)); |
| } |
| } |
| |
| public C get(int index) { |
| return this.elements.get(index); |
| } |
| |
| public ListIterator<C> iterator() { |
| return IteratorTools.clone(this.elements); |
| } |
| |
| /** |
| * Return the size of the context elements collection |
| */ |
| public int size() { |
| return this.elements.size(); |
| } |
| |
| /** |
| * Add a context element for the specified resource element at the |
| * specified index. |
| */ |
| public C addContextElement(int index, R resourceElement) { |
| return this.add(index, this.adapter.buildContextElement(resourceElement)); |
| } |
| |
| /** |
| * Add the specified context element to the container at the specified |
| * index. |
| */ |
| protected abstract C add(int index, C element); |
| |
| /** |
| * Add context elements for the specified resource elements at the |
| * specified index. |
| */ |
| public Iterable<C> addContextElements(int index, Iterable<R> resourceElements) { |
| ArrayList<C> contextElements = new ArrayList<>(); |
| for (R resourceElement : resourceElements) { |
| contextElements.add(this.adapter.buildContextElement(resourceElement)); |
| } |
| return this.addAll(index, contextElements); |
| } |
| |
| /** |
| * Add the specified context elements to the collection. |
| */ |
| protected abstract Iterable<C> addAll(int index, Iterable<C> contextElements); |
| |
| /** |
| * Remove the specified context element from the container. |
| */ |
| public abstract void remove(C element); |
| |
| /** |
| * Remove the specified context elements from the container. |
| */ |
| public abstract void removeAll(Iterable<C> contextElements); |
| |
| protected abstract void move(int index, C element); |
| |
| @Override |
| public String toString() { |
| return this.elements.toString(); |
| } |
| |
| /** |
| * Adapter used by the container to convert resource and context |
| * elements. |
| */ |
| public interface Adapter<C, R> { |
| /** |
| * Return the current list of resource elements. |
| * The context elements are synchronized with this list. |
| */ |
| Iterable<R> getResourceElements(); |
| |
| /** |
| * Return the specified context element's resource element. |
| * @see #buildContextElement(Object) |
| */ |
| R extractResourceElement(C contextElement); |
| |
| /** |
| * Build a context element for the specified resource element. |
| * @see #extractResourceElement(Object) |
| */ |
| C buildContextElement(R resourceElement); |
| } |
| } |
| |
| public abstract class AbstractContainerAdapter<C, R> |
| implements Container.Adapter<C, R> |
| { |
| @Override |
| public String toString() { |
| return ObjectTools.toString(this); |
| } |
| } |
| |
| |
| /** |
| * Adapter used to synchronize a collection of context models with the |
| * corresponding collection of resource models. |
| * @param <C> the type of context elements |
| * @param <R> the type of resource elements |
| */ |
| public abstract class ContextContainer<C extends JpaContextModel, R> |
| extends Container<C, R> |
| { |
| protected ContextContainer(String aspectName, Container.Adapter<C, R> adapter) { |
| super(aspectName, adapter); |
| } |
| |
| /** |
| * Synchronize the context container with its |
| * corresponding resource container: moving, removing, and adding elements |
| * as necessary. |
| * <p> |
| * We can do this because:<ul> |
| * <li>the XML translators will <em>move</em> the EMF elements when |
| * appropriate (as opposed to simply rebuilding them in place). |
| * <li>the Java resource model will re-use existing annotations when |
| * appropriate (as opposed to simply rebuilding them in place). |
| * </ul> |
| */ |
| public void synchronizeWithResourceModel(IProgressMonitor monitor) { |
| this.sync(true, monitor); // true = sync |
| } |
| |
| /** |
| * @see #synchronizeWithResourceModel(IProgressMonitor) |
| */ |
| public void update(IProgressMonitor monitor) { |
| this.sync(false, monitor); // false = update |
| } |
| |
| /** |
| * The specified <code>sync</code> flag controls whether any surviving |
| * context nodes are either <em>synchronized</em> (<code>true</code>) or |
| * <em>updated</em> (<code>false</code>). |
| */ |
| protected void sync(boolean sync, IProgressMonitor monitor) { |
| @SuppressWarnings("unchecked") |
| HashSet<C> contextElements = (HashSet<C>) CollectionTools.hashSet(this.elements.toArray()); |
| ArrayList<C> contextElementsToSync = new ArrayList<>(contextElements.size()); |
| int resourceIndex = 0; |
| |
| for (R resourceElement : this.adapter.getResourceElements()) { |
| boolean match = false; |
| for (Iterator<C> stream = contextElements.iterator(); stream.hasNext(); ) { |
| C contextElement = stream.next(); |
| if (ObjectTools.equals(this.adapter.extractResourceElement(contextElement), resourceElement)) { |
| // we don't know the source index because the element has been moved by previously moved elements |
| this.move(resourceIndex, contextElement); |
| stream.remove(); |
| // TODO perform update here someday... |
| contextElementsToSync.add(contextElement); |
| match = true; |
| break; |
| } |
| } |
| if ( ! match) { |
| // added elements are sync'ed during construction or will be |
| // updated during the next "update" (which is triggered by |
| // their addition to the model) |
| this.addContextElement(resourceIndex, resourceElement); |
| } |
| resourceIndex++; |
| } |
| // remove any leftover context elements |
| for (C contextElement : contextElements) { |
| this.remove(contextElement); |
| } |
| // TODO bjv |
| // take care of the structural changes before sync'ing the remaining elements; |
| // we might be able to do this inline once we get rid of the "list change" events |
| // and go with only add, remove, etc. list events |
| // (these syncs will trigger "list change" events with list aspect adapters, which |
| // trigger refreshes of UI adapters (e.g. TableModelAdapter) which will prematurely |
| // discover any structural changes... :( ) |
| // see ItemAspectListValueModelAdapter.itemAspectChanged(EventObject) |
| for (C contextElement : contextElementsToSync) { |
| if (sync) { |
| contextElement.synchronizeWithResourceModel(monitor); |
| } else { |
| contextElement.update(monitor); |
| } |
| } |
| } |
| } |
| |
| |
| /** |
| * Adapter used to synchronize a <em>collection</em> of context models with the |
| * corresponding <em>collection</em> of resource models. |
| * @param <C> the type of context elements |
| * @param <R> the type of resource elements |
| */ |
| public abstract class ContextCollectionContainer<C extends JpaContextModel, R> |
| extends ContextContainer<C, R> |
| { |
| protected ContextCollectionContainer(String aspectName, Container.Adapter<C, R> adapter) { |
| super(aspectName, adapter); |
| } |
| |
| @Override |
| protected C add(int index, C element) { |
| // ignore the index - not a list |
| AbstractJpaContextModel.this.addItemToCollection(element, this.elements, this.aspectName); |
| return element; |
| } |
| |
| @Override |
| protected Iterable<C> addAll(int index, Iterable<C> contextElements) { |
| // ignore the index - not a list |
| AbstractJpaContextModel.this.addItemsToCollection(contextElements, this.elements, this.aspectName); |
| return contextElements; |
| } |
| |
| @Override |
| public void remove(C element) { |
| AbstractJpaContextModel.this.removeItemFromCollection(element, this.elements, this.aspectName); |
| } |
| |
| @Override |
| public void removeAll(Iterable<C> contextElements) { |
| AbstractJpaContextModel.this.removeItemsFromCollection(contextElements, this.elements, this.aspectName); |
| } |
| |
| @Override |
| protected void move(int index, C element) { |
| // NOP - not a list |
| } |
| } |
| |
| // open up accessibility to container classes |
| @Override |
| protected <E> boolean addItemToCollection(E item, Collection<E> collection, String collectionName) { |
| return super.addItemToCollection(item, collection, collectionName); |
| } |
| @Override |
| protected <E> boolean addItemsToCollection(Iterable<? extends E> items, Collection<E> collection, String collectionName) { |
| return super.addItemsToCollection(items, collection, collectionName); |
| } |
| @Override |
| protected boolean removeItemFromCollection(Object item, Collection<?> collection, String collectionName) { |
| return super.removeItemFromCollection(item, collection, collectionName); |
| } |
| @Override |
| protected boolean removeItemsFromCollection(Iterable<?> items, Collection<?> collection, String collectionName) { |
| return super.removeItemsFromCollection(items, collection, collectionName); |
| } |
| |
| |
| /** |
| * Use this container for a collection of <em>specified</em> elements. |
| * The container is initialized from the resource model upon construction. |
| * @see AbstractJpaContextModel.SpecifiedContextListContainer |
| * @see #buildSpecifiedContextCollectionContainer(String, AbstractJpaContextModel.Container.Adapter) |
| */ |
| public class SpecifiedContextCollectionContainer<C extends JpaContextModel, R> |
| extends ContextCollectionContainer<C, R> |
| { |
| public SpecifiedContextCollectionContainer(String aspectName, Container.Adapter<C, R> adapter) { |
| super(aspectName, adapter); |
| this.initialize(); |
| } |
| } |
| |
| protected <C extends JpaContextModel, R> SpecifiedContextCollectionContainer<C, R> buildSpecifiedContextCollectionContainer(String aspectName, Container.Adapter<C, R> adapter) { |
| return new SpecifiedContextCollectionContainer<>(aspectName, adapter); |
| } |
| |
| |
| /** |
| * Use this container for a collection of <em>virtual</em> elements |
| * (e.g. default elements). |
| * The container is <em>not</em> initialized from the resource model upon |
| * construction (i.e. the container will effectively be initialized with the |
| * first <em>update</em>). |
| * @see AbstractJpaContextModel.VirtualContextListContainer |
| * @see #buildVirtualContextCollectionContainer(String, AbstractJpaContextModel.Container.Adapter) |
| */ |
| public class VirtualContextCollectionContainer<C extends JpaContextModel, R> |
| extends ContextCollectionContainer<C, R> |
| { |
| public VirtualContextCollectionContainer(String aspectName, Container.Adapter<C, R> adapter) { |
| super(aspectName, adapter); |
| // NO initialization |
| } |
| } |
| |
| protected <C extends JpaContextModel, R> VirtualContextCollectionContainer<C, R> buildVirtualContextCollectionContainer(String aspectName, Container.Adapter<C, R> adapter) { |
| return new VirtualContextCollectionContainer<>(aspectName, adapter); |
| } |
| |
| |
| /** |
| * Adapter used to synchronize a <em>list</em> of context models with the |
| * corresponding <em>list</em> of resource models. |
| * @param <C> the type of context elements |
| * @param <R> the type of resource elements |
| */ |
| public abstract class ContextListContainer<C extends JpaContextModel, R> |
| extends ContextContainer<C, R> |
| { |
| protected ContextListContainer(String aspectName, Container.Adapter<C, R> adapter) { |
| super(aspectName, adapter); |
| } |
| |
| /** |
| * Return the index of the specified context element. |
| */ |
| public int indexOf(C element) { |
| return this.elements.indexOf(element); |
| } |
| |
| @Override |
| protected C add(int index, C element) { |
| AbstractJpaContextModel.this.addItemToList(index, element, this.elements, this.aspectName); |
| return element; |
| } |
| |
| @Override |
| public Iterable<C> addAll(int index, Iterable<C> contextElements) { |
| AbstractJpaContextModel.this.addItemsToList(index, contextElements, this.elements, this.aspectName); |
| return contextElements; |
| } |
| |
| /** |
| * Move the context element at the specified source index to the |
| * specified target index. |
| */ |
| public void move(int targetIndex, int sourceIndex) { |
| AbstractJpaContextModel.this.moveItemInList(targetIndex, sourceIndex, this.elements, this.aspectName); |
| } |
| |
| @Override |
| public void move(int index, C element) { |
| AbstractJpaContextModel.this.moveItemInList(index, element, this.elements, this.aspectName); |
| } |
| |
| /** |
| * Clear the container. |
| */ |
| public void clear() { |
| AbstractJpaContextModel.this.clearList(this.elements, this.aspectName); |
| } |
| |
| /** |
| * Remove the context element at the specified index from the container. |
| */ |
| public C remove(int index) { |
| return AbstractJpaContextModel.this.removeItemFromList(index, this.elements, this.aspectName); |
| } |
| |
| @Override |
| public void remove(C element) { |
| AbstractJpaContextModel.this.removeItemFromList(element, this.elements, this.aspectName); |
| } |
| |
| @Override |
| public void removeAll(Iterable<C> contextElements) { |
| AbstractJpaContextModel.this.removeItemsFromList(contextElements, this.elements, this.aspectName); |
| } |
| } |
| |
| // open up accessibility to container classes |
| @Override |
| protected <E> void addItemToList(int index, E item, List<E> list, String listName) { |
| super.addItemToList(index, item, list, listName); |
| } |
| @Override |
| protected <E> boolean addItemsToList(int index, Iterable<? extends E> items, List<E> list, String listName) { |
| return super.addItemsToList(index, items, list, listName); |
| } |
| @Override |
| protected <E> void moveItemInList(int targetIndex, int sourceIndex, List<E> list, String listName) { |
| super.moveItemInList(targetIndex, sourceIndex, list, listName); |
| } |
| @Override |
| protected <E> void moveItemInList(int targetIndex, E item, List<E> list, String listName) { |
| super.moveItemInList(targetIndex, item, list, listName); |
| } |
| @Override |
| protected boolean clearList(List<?> list, String listName) { |
| return super.clearList(list, listName); |
| } |
| @Override |
| protected <E> E removeItemFromList(int index, List<E> list, String listName) { |
| return super.removeItemFromList(index, list, listName); |
| } |
| @Override |
| protected boolean removeItemFromList(Object item, List<?> list, String listName) { |
| return super.removeItemFromList(item, list, listName); |
| } |
| @Override |
| protected boolean removeItemsFromList(Iterable<?> items, List<?> list, String listName) { |
| return super.removeItemsFromList(items, list, listName); |
| } |
| |
| |
| /** |
| * @see AbstractJpaContextModel.SpecifiedContextCollectionContainer |
| * @see #buildSpecifiedContextListContainer(String, AbstractJpaContextModel.Container.Adapter) |
| */ |
| public class SpecifiedContextListContainer<C extends JpaContextModel, R> |
| extends ContextListContainer<C, R> |
| { |
| public SpecifiedContextListContainer(String aspectName, Container.Adapter<C, R> adapter) { |
| super(aspectName, adapter); |
| this.initialize(); |
| } |
| } |
| |
| protected <C extends JpaContextModel, R> SpecifiedContextListContainer<C, R> buildSpecifiedContextListContainer(String aspectName, Container.Adapter<C, R> adapter) { |
| return new SpecifiedContextListContainer<>(aspectName, adapter); |
| } |
| |
| |
| /** |
| * @see AbstractJpaContextModel.VirtualContextCollectionContainer |
| * @see #buildVirtualContextListContainer(String, AbstractJpaContextModel.Container.Adapter) |
| */ |
| public class VirtualContextListContainer<C extends JpaContextModel, R> |
| extends ContextListContainer<C, R> |
| { |
| public VirtualContextListContainer(String aspectName, Container.Adapter<C, R> adapter) { |
| super(aspectName, adapter); |
| // NO initialization |
| } |
| } |
| |
| protected <C extends JpaContextModel, R> VirtualContextListContainer<C, R> buildVirtualContextListContainer(String aspectName, Container.Adapter<C, R> adapter) { |
| return new VirtualContextListContainer<>(aspectName, adapter); |
| } |
| |
| |
| /** |
| * Adapter used to synchronize a <em>list</em> of context values with the |
| * corresponding list of resource values. The assumption is the values are |
| * equal in each model (e.g. {@link String}s). |
| * @param <E> the type of context elements |
| */ |
| public abstract class ValueListContainer<E> |
| extends Container<E, E> |
| { |
| protected ValueListContainer(String aspectName, Container.Adapter<E, E> adapter) { |
| super(aspectName, adapter); |
| } |
| |
| /** |
| * Return the index of the specified context element. |
| */ |
| public int indexOf(E element) { |
| return this.elements.indexOf(element); |
| } |
| |
| @Override |
| protected E add(int index, E element) { |
| AbstractJpaContextModel.this.addItemToList(index, element, this.elements, this.aspectName); |
| return element; |
| } |
| |
| @Override |
| protected Iterable<E> addAll(int index, Iterable<E> contextElements) { |
| AbstractJpaContextModel.this.addItemsToList(index, contextElements, this.elements, this.aspectName); |
| return contextElements; |
| } |
| |
| /** |
| * Move the context element at the specified source index to the |
| * specified target index. |
| */ |
| public void move(int targetIndex, int sourceIndex) { |
| AbstractJpaContextModel.this.moveItemInList(targetIndex, sourceIndex, this.elements, this.aspectName); |
| } |
| |
| @Override |
| public void move(int index, E element) { |
| AbstractJpaContextModel.this.moveItemInList(index, element, this.elements, this.aspectName); |
| } |
| |
| @Override |
| public void remove(E element) { |
| AbstractJpaContextModel.this.removeItemFromList(element, this.elements, this.aspectName); |
| } |
| |
| /** |
| * Remove the context element at the specified index from the container. |
| */ |
| public E remove(int index) { |
| return AbstractJpaContextModel.this.removeItemFromList(index, this.elements, this.aspectName); |
| } |
| |
| @Override |
| public void removeAll(Iterable<E> contextElements) { |
| AbstractJpaContextModel.this.removeItemsFromList(contextElements, this.elements, this.aspectName); |
| } |
| |
| public void synchronizeWithResourceModel() { |
| int resourceIndex = 0; |
| // TODO we don't look for any moves, just simple adds and removes |
| for (E resourceElement : this.adapter.getResourceElements()) { |
| if (this.size() > resourceIndex) { |
| E contextElement = this.get(resourceIndex); |
| if (ObjectTools.notEquals(this.adapter.extractResourceElement(contextElement), resourceElement)) { |
| this.addContextElement(resourceIndex, resourceElement); |
| } |
| } else { |
| this.addContextElement(resourceIndex, resourceElement); |
| } |
| resourceIndex++; |
| } |
| |
| while (resourceIndex < this.size()) { |
| this.remove(resourceIndex++); |
| } |
| } |
| } |
| } |