/*
* 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.impl;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.edit.domain.EditingDomain;
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.EFeatureIDFactory;
import org.geotools.data.efeature.EFeatureInfo;
import org.geotools.data.efeature.internal.EFeatureVoidIDFactory;
import org.geotools.util.logging.Logging;
/**
* Default implementation of {@link EFeatureContext} instance.
* <p>
* This implementation generates unique IDs for
* each {@link EAttribute} is manages.
* </p>
*
* @author kengu - 26. mai 2011
*
*/
public final class EFeatureContextImpl implements EFeatureContext {
private static final Logger LOGGER = Logging.getLogger(EFeatureContextImpl.class);
// -----------------------------------------------------
// EFeatureContext implementation
// -----------------------------------------------------
private final String eContextID;
private final boolean isPrototype;
private final EFeatureIDFactory eIDFactory;
private final WeakReference<EFeatureContextFactory> eContextFactory;
private final ResourceHandler eResourceHandler = new ResourceHandler();
private final Map<String, EPackage> ePackageMap = new HashMap<String, EPackage>();
private final Map<String, EditingDomain> eDomainMap = new HashMap<String, EditingDomain>();
// -----------------------------------------------------
// Constructors
// -----------------------------------------------------
/**
* This constructor implements a the default
* {@link EFeatureIDFactoryImpl EFeature ID factory}
* </p>
* @param eContextFactory - the {@link EFeatureContext eContextID factory}
* @param eContextID - the {@link EFeatureContext eContextID} which this is created with
* @throws NullPointerException - if any argument is <code>null</code>
* @throws IllegalArgumentException - if 'eContextID' is an empty string.
*/
public EFeatureContextImpl(EFeatureContextFactory eContextFactory, String eContextID) {
this(eContextFactory,eContextID,new EFeatureIDFactoryImpl());
}
/**
* This constructor use given
* {@link EFeature} ID {@link EFeatureIDFactoryImpl factory}
* <p>
* @param eContextFactory - the {@link EFeatureContext eContextID factory}
* @param eContextID - the {@link EFeatureContext eContextID} which this is created with
* @param eIDFactory - the {@link EFeatureIDFactory eFeatureID factory}
* @throws NullPointerException - if any argument is <code>null</code>
* @throws IllegalArgumentException - if 'eContextID' is an empty string.
*/
public EFeatureContextImpl(EFeatureContextFactory eContextFactory,
String eContextID, EFeatureIDFactory eIDFactory)
throws NullPointerException, IllegalArgumentException {
//
// Is valid?
//
if(eContextID==null) {
throw new NullPointerException("Context ID can not be null");
}
if(eContextID.length()==0) {
throw new IllegalArgumentException("Context ID can not be empty");
}
if(eContextFactory==null) {
throw new NullPointerException("Context factory can not be null");
}
if(eIDFactory==null) {
throw new NullPointerException("EFeatureIDFactory can not be null");
}
//
// Cache properties
//
this.eContextID = eContextID;
this.eIDFactory = eIDFactory;
this.isPrototype = (eIDFactory instanceof EFeatureVoidIDFactory);
this.eContextFactory = new WeakReference<EFeatureContextFactory>(eContextFactory);
}
// -----------------------------------------------------
// EFeatureContext implementation
// -----------------------------------------------------
@Override
public String eContextID() {
return eContextID;
}
@Override
public boolean isPrototype() {
return isPrototype;
}
@Override
public EFeatureContextInfo eStructure() {
return eContextFactory().eStructure(eContextID);
}
@Override
public EFeatureIDFactory eIDFactory() {
return eIDFactory;
}
@Override
public EFeatureContextFactory eContextFactory() {
return eContextFactory.get();
}
@Override
public boolean containsDomain(String eDomainID) {
return eDomainMap.containsKey(eDomainID);
}
@Override
public List<String> eDomainIDs() {
return Collections.unmodifiableList(new ArrayList<String>(eDomainMap.keySet()));
}
@Override
public EditingDomain eGetDomain(String eDomainID) {
EditingDomain eDomain = eDomainMap.get(eDomainID);
if(eDomain==null) {
throw new IllegalArgumentException("EditingDomain [" + eDomainID + "] not found");
}
return eDomain;
}
@Override
public Resource eGetResource(String eDomainID, URI eURI, boolean loadOnDemand) {
return eGetDomain(eDomainID).getResourceSet().getResource(eURI, loadOnDemand);
}
@Override
public boolean containsPackage(String eNsURI) {
return ePackageMap.containsKey(eNsURI);
}
@Override
public List<String> eNsURIs() {
return Collections.unmodifiableList(new ArrayList<String>(ePackageMap.keySet()));
}
@Override
public EPackage eGetPackage(String eNsURI) {
EPackage ePackage = ePackageMap.get(eNsURI);
if(ePackage==null) {
throw new IllegalArgumentException("EPackage [" + eNsURI + "] not found");
}
return ePackage;
}
@Override
public boolean contains(EPackage ePackage) {
return ePackageMap.containsKey(ePackage.getNsURI());
}
@Override
public EPackage eAdd(EPackage ePackage) throws IllegalArgumentException {
String eNsURI = ePackage.getNsURI();
if(!(eNsURI==null || eNsURI.length()==0)) {
return ePackageMap.put(eNsURI, ePackage);
}
throw new IllegalArgumentException("EPackage#getNsURI() must be specified.");
}
public boolean contains(String eDomainID, EditingDomain eDomain) {
return eDomainMap.containsKey(eDomainID);
}
@Override
public EditingDomain eAdd(String eDomainID, EditingDomain eDomain) throws IllegalArgumentException {
if(!(eDomainID==null || eDomainID.length()==0)) {
ResourceSet eSet = eDomain.getResourceSet();
eSet.eAdapters().add(eResourceHandler);
return eDomainMap.put(eDomainID, eDomain);
}
throw new IllegalArgumentException("EditingDomain ID must be specified.");
}
@Override
public EFeatureInfo eAdapt(EFeature eFeature, boolean copy) {
//
// Forward to structure
//
EFeatureInfo eInfo = eStructure().eAdapt(eFeature.getStructure(), copy);
//
// Update structure
//
eFeature.setStructure(eInfo);
//
// Finished
//
return eInfo;
}
// -----------------------------------------------------
// Inner classes
// -----------------------------------------------------
private class ResourceHandler extends AdapterImpl {
@Override
public void notifyChanged(Notification msg) {
//
// Get ID factory
//
EFeatureIDFactory eIDFactory = eIDFactory();
//
// Does this context generate IDs?
//
if( !(eIDFactory instanceof EFeatureVoidIDFactory) ) {
//
// Is added or removed?
//
if( msg.getEventType() == Notification.ADD ) {
Object value = msg.getNewValue();
if(value instanceof EObject) {
//
// Implements EFeature?
//
if(value instanceof EFeature) {
//
// Cast to EFeature
//
EFeature eFeature = (EFeature)value;
//
// ----------------------------------------------------------
// Adapt given EFeature (does nothing if already in context)
// ----------------------------------------------------------
// This is a very important step: It's part of the context
// startup problem solution (see EFeatureContextHelper),
// and ensures that context-unaware objects become
// aware of the context they are added to. Without this
// step, EFeature stays context-unaware, preventing
// EFeatureReaders from reading them using a context ID
// known by client code.
// ----------------------------------------------------------
//
eAdapt(eFeature, true);
//
// Get current ID
//
String eID = eFeature.getID();
//
// Resolve unique ID
//
if( !( eID==null || eID.length()==0 ) ) {
eIDFactory.useID(eFeature, eID);
} else {
eIDFactory.createID(eFeature);
}
} else if(eIDFactory.creates((EObject)value)) {
//
// Adapt object directly to context
//
eStructure().eAdapt((EObject)value);
}
}
else if(value instanceof Resource) {
((Resource)value).eAdapters().add(this);
}
} else if( msg.getEventType() == Notification.REMOVE ) {
Object value = msg.getOldValue();
if(value instanceof EObject) {
//
// Cast to EObject
//
EObject eObject = (EObject)value;
//
// ID created for this object?
//
if(eIDFactory.contains(eObject)) {
try {
//
// This should never fail...
//
eIDFactory.disposeID(eObject);
} catch (IllegalArgumentException e) {
//
// ... but if it does, log a warning ...
//
LOGGER.log(Level.WARNING, e.getMessage(), e);
//
// ... and re-throw it
//
throw e;
}
}
//eIDFactory.disposeID(eFeature.getID());
}
else if(value instanceof Resource) {
((Resource)value).eAdapters().remove(this);
}
}
}
}
}
}