/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2011, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.data.efeature.internal;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.geotools.data.efeature.EFeature;
import org.geotools.data.efeature.EFeatureContext;
import org.geotools.data.efeature.EFeatureContextFactory;
import org.geotools.data.efeature.EFeatureContextInfo;
import org.geotools.data.efeature.EFeatureHints;
import org.geotools.data.efeature.EFeatureInfo;
import org.geotools.data.efeature.EFeatureReader;
import org.geotools.data.efeature.EFeatureStatus;
import com.vividsolutions.jts.geom.Geometry;
/**
* This helper class implements a solution to <i>the context startup problem</i>.
* <p>
* <b>The context startup problem</b>:
* <p>
* <ol>
* <li>Since multiple {@link EFeatureContextFactory} instances are allowed,
* a {@link EFeatureContext context} ID is ambiguous without knowing
* which factory created the actual context.</li>
* <li>Since EMF {@link EPackage}s are completely unaware of the
* {@link EFeatureContext context} during the construction of
* {@link EOBject}s from {@link EClass}s, the actual
* {@link EFeatureInfo structure} (and context) remains unknown until
* it is {@link EFeature#setStructure(EFeatureInfo) explicitly set}.</li>
* </ol>
* </p>
* <b>Solution</b>
* <p>
* The problem is solved by an internal
* {@link EFeatureContext#isPrototype() prototype context}
* which {@link EFeatureInternal} objects belongs to during context-unaware
* construction. This context {@link EFeatureContextInfo#eFeatureInfoCache() caches}
* all the {@link EFeatureInfo structures} generated from {@link EObject} during the
* context-unaware construction. Only objects that implements {@link EFeature}
* or contains {@link Geometry EFeature compatible data} are added to the
* {@link EFeatureInfoCache cache}.
* <p>
* Each {@link EFeatureInternal} instance use the structure returned by the
* {@link EFeatureContextHelper helper}, until the actual context is known.
* <p>
* When the actual structure is known, {@link EFeatureInternal#setStructure(EFeatureInfo)}
* is called, mapping the {@link EObject} to given structure.
* <p>
* The {@link EFeatureReader} does this during {@link EObject} iteration.
* </p>
* <b>NOTE</b>: This solution adds some memory overhead since one additional
* {@link EFeatureInfo} instance is created for each unique {@link EClass} added
* to a {@link EFeatureContext context}.
* </p>
*
* @author kengu - 29. mai 2011
*
*/
public final class EFeatureContextHelper {
public static final String INTERNAL_CONTEXT_ID = EFeatureContextHelper.class.getName();
private static final EFeatureContext eContext;
private static final EFeatureContextFactory eContextFactory;
// -----------------------------------------------------
// Static construction
// -----------------------------------------------------
static {
eContextFactory = new EFeatureContextFactory();
eContext = eContextFactory.create(INTERNAL_CONTEXT_ID, new EFeatureVoidIDFactory(), new EFeatureHints());
}
// -----------------------------------------------------
// EFeatureContextHelper methods
// -----------------------------------------------------
/**
* Get internal {@link EFeatureContext} instance.
*/
public static final EFeatureContext eContext() {
return eContext;
}
/**
* Get internal context {@link EFeatureContext} instance.
* <p>
* If not already added, this method adds the {@link EPackage},
* which given object belongs, to the context.
* @param eObject - given {@link EObject} instance
* @return the {@link EFeatureContext} instance cached by this helper
* @throws IllegalArgumentException If new package fails to validate.
*/
public static final EFeatureContext eContext(EObject eObject) {
//
// Get package
//
EPackage ePackage = eObject.eClass().getEPackage();
//
// Add package to internal context
//
eAdd(ePackage);
//
// Finished
//
return eContext;
}
/**
* Get the {@link EFeatureInfo#isPrototype() prototype}
* {@link EFeatureInfo} instance cached for the {@link EClass}
* defining given {@link EObject}.
* </p>
* @param eObject - the {@link EObject} instance
* @return a {@link EFeatureInfo} if found, <code>null</code> otherwise.
*/
public static final EFeatureInfo ePrototype(EObject eObject) {
return eContext.eStructure().eGetFeatureInfo(eObject);
}
/**
* Get the {@link EFeatureInfo#isPrototype() prototype}
* {@link EFeatureInfo} instance cached for given {@link EClass}.
* </p>
* @param eClass - the {@link EClass} instance
* @return a {@link EFeatureInfo} if found, <code>null</code> otherwise.
*/
public static final EFeatureInfo ePrototype(EClass eClass) {
return eContext.eStructure().eGetFeatureInfo(eClass);
}
// -----------------------------------------------------
// Helper methods
// -----------------------------------------------------
/**
* Add {@link EPackage} instance to context.
* <p>
* @param ePackage - the {@link EPackage} instance
* @return <code>null</code> if not already registered, or the
* {@link EPackage} replaced by given instance.
* @throws IllegalStateException If no
* {@link EPackage#getNsURI() EPackage name space URI} is specified
* @throws IllegalArgumentException If new package fails to validate.
*/
private static final EPackage eAdd(EPackage ePackage) throws IllegalArgumentException {
EPackage eOldPackage = eContext.eAdd(ePackage);
if(eOldPackage!=ePackage) {
EFeatureStatus eStatus = eContext().eStructure().validate();
if(eStatus.isFailure()) {
throw new IllegalArgumentException("EPackage not properly added. "
+ eStatus.getMessage(),eStatus.getCause());
}
}
return eOldPackage;
}
}