Package org.geotools.data.efeature.impl

Source Code of org.geotools.data.efeature.impl.EFeatureIDFactoryImpl

/*
*    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.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.geotools.data.efeature.EFeature;
import org.geotools.data.efeature.EFeatureIDFactory;
import org.geotools.data.efeature.EFeaturePackage;
import org.geotools.data.efeature.internal.EFeatureDelegate;
import org.geotools.util.WeakHashSet;
import org.geotools.util.logging.Logging;

/**
* Default implementation of {@link EFeatureIDFactory} instance.
* <p>
* This implementation generates unique IDs for
* each {@link EAttribute} is manages.
* @author kengu - 26. mai 2011
*
*
* @source $URL$
*/
public class EFeatureIDFactoryImpl implements EFeatureIDFactory {
   
    public static final String DEFAULT_PREFIX = "F";
   
    private static final Logger LOGGER = Logging.getLogger(EFeatureIDFactoryImpl.class);
   
    protected String ePrefix;
   
    /**
     * This map contains a mapping from each EObject to the ID created for it by this factory.
     */
    protected Map<URI,WeakHashMap<EObject,Long>> eInverseIDMap = new HashMap<URI,WeakHashMap<EObject, Long>>();

    /**
     * This map contains a mapping from each ID to each {@link EObject} assigned to it.
     * <b>NOTE</b>: Only objects already assigned an ID are allowed to have
     * the same ID. This only happens if multiple {@link EObject}s are constructed
     * from the same EMF {@link Resource}. This occurs when {@link EObject} are queried
     * multiple times from the resource, and the garbage collector has not yet disposed
     * older {@link EObject} instances not longer referenced by any client code.
     * A {@link WeakHashSet} is used to ensure that this factory does not prevent the
     * garbage collection.
     */
    protected Map<URI,Map<Long,WeakHashSet<EObject>>> eIDMap = new HashMap<URI,Map<Long, WeakHashSet<EObject>>>();
   
    /**
     * This map contains the next ID for each {@link EAttribute} this factory create IDs for.
     * It allows for faster creation of unique IDs for each {@link EAttribute} by removing
     * the step of calculating maximum ID value among existing IDs.
     */
    protected Map<URI,Long> eNextIDMap = new HashMap<URI,Long>();
   
    /**
     * This map ensures that only one {@link EAttribute} per {@link EClass} is allowed
     * to be used as an unique ID. This is introduced to reduce the complexity, but could be
     * relaxed if necessary.
     *
     * TODO: Add option to allow multiple ID attribute registrations per {@link EClass}?
     */
    protected Map<EClass,EAttribute> eAttributeMap = new HashMap<EClass, EAttribute>();
   
    // -----------------------------------------------------
    //  Constructors
    // -----------------------------------------------------
   
    /**
     * Default constructor.
     * <p>
     * This factory only handles {@link EObject} instances which
     * implements the {@link EFeature} interface.
     * @see {@link EFeaturePackage#getEFeature_ID()}
     * @throws IllegalArgumentException If the
     * {@link EDataType#getInstanceClass() data type} of one or
     * more {@link EAttribute#getEAttributeType()}s are not {@link String}.
     */
    public EFeatureIDFactoryImpl() throws IllegalArgumentException {
        this(DEFAULT_PREFIX, EFeaturePackage.eINSTANCE.getEFeature_ID());
       
       
    }

    /**
     * Constructor which tells this factory to generate IDs
     * for given {@link EAttribute} instances.
     * @param ePrefix - custom ID prefix
     * @param eAttributes - array of {@link EAttribute} instances
     * @see {@link EFeaturePackage#getEFeature_ID()}
     * @throws IllegalArgumentException If the
     * {@link EDataType#getInstanceClass() data type} of one or
     * more {@link EAttribute#getEAttributeType()}s are not {@link String}.
     */   
    public EFeatureIDFactoryImpl(String ePrefix, EAttribute... eAttributes)
        throws IllegalArgumentException {
        this.ePrefix = ePrefix;
        for(EAttribute it : eAttributes) {
            add(it.getEContainingClass(), it);
        }
    }
   
    // -----------------------------------------------------
    //  EFeatureIDFactory implementation
    // -----------------------------------------------------
   
    @Override
    public String getPrefix() {
        return ePrefix;
    }
       
    @Override
    public EAttribute add(EObject eObject) throws IllegalArgumentException {
        return add(eImpl(eObject).eClass());
    }

    @Override
    public EAttribute add(EClass eClass) throws IllegalArgumentException {
        //
        // Check if have ID attribute?       
        //
        EAttribute eAtt = eClass.getEIDAttribute();
        if(eAtt==null) {
            for(EAttribute it : eClass.getEAllAttributes()) {
                String eType = it.getEAttributeType().getInstanceClassName();
                if("java.lang.String".equals(eType)) {
                    eAtt = it;
                    break;
                }
            }
        }
        //
        // verify non-null
        //
        if(eAtt==null) {
            throw new IllegalArgumentException("Did not find " +
                "any EAttribute ID in " + eClass.getName());
        }
        //
        // Add attribute
        //
        add(eClass,eAtt);
        //
        // Finished
        //
        return eAtt;
    }

    @Override
    public void add(EClass eClass, EAttribute eID) {
        EAttribute eAtt = eAttributeMap.get(eClass);
        if(eAtt!=null) {
            if(eAtt!=eID) {
                throw new IllegalArgumentException("EAttribute " +
                        eAtt.getName() + " already as ID added for " +
                        "EClass " + eClass.getName());
            }
        }
        else {
            String eType = eID.getEAttributeType().getInstanceClassName();
            if(!"java.lang.String".equals(eType)) {
                throw new IllegalArgumentException("EAttribute " +
                        eID.getName() + " contains " + eType + ", not java.lang.String");
            }           
            eAttributeMap.put(eClass, eID);
        }       
    }   
       
    @Override
    public boolean creates(EObject eObject) {
        return creates(eImpl(eObject).eClass());
    }

    @Override
    public boolean creates(EClass eClass) {
        return eAttributeMap.containsKey(eClass);
    }
   
    @Override
    public boolean creates(EAttribute eAttribute) {
        return eAttributeMap.containsValue(eAttribute);
    }
       
    @Override
    public boolean contains(URI eURI) {
        return eIDMap.containsKey(eURI);
    }

    @Override
    public boolean contains(EObject eObject) {
        EObject eImpl = eImpl(eObject);
        for(Map<EObject,Long> it : eInverseIDMap.values()) {
            if(it.containsKey(eImpl)) {
                return true;
            }
        }
        return false;
    }
   
    @Override
    public Map<EObject,String> getIDMap(URI eURI) {
        Map<EObject,Long> eCachedIDs = eInverseIDMap.get(eURI);
        if(eCachedIDs!=null) {
            WeakHashMap<EObject,String> eIDs = new WeakHashMap<EObject, String>(eCachedIDs.size());
            for(Entry<EObject,Long> it : eCachedIDs.entrySet()) {
                eIDs.put(it.getKey(), ePrefix + it.getValue());
            }
            return eIDs;
        }
        return Collections.emptyMap();
    }
   
    @Override
    public String getID(EObject eObject) {
        //
        // Get implementation (strips away EFeatureDelegates)
        //
        EObject eImpl = eImpl(eObject);
        //
        // Verify EObject
        //
        verify("EObject", eImpl, true, false, true);
        //
        // Get URI
        //
        URI eURI = eImpl.eResource().getURI();
        //
        // Forward
        //
        return eGetID(eURI,eImpl);
    }
   
    @Override
    public String createID(EObject eObject) throws IllegalArgumentException {
        //
        // Get implementation (strips away EFeatureDelegates)
        //
        EObject eImpl = eImpl(eObject);
        //
        // Verify EObject
        //
        verify("eObject", eImpl, true, true, true);
        //
        // Get ID attribute
        //
        EAttribute eAttribute = eAttributeMap.get(eImpl.eClass());
        //
        // Verify
        //
        verify("eAttribute",eAttribute,true);
        //
        // Get URI
        //
        URI eURI = eImpl.eResource().getURI();       
        //
        // Verify
        //
        verify("eURI",eURI,true);
        //
        // Get set value
        //
        String eSetID = eGetID(eURI, eImpl);
        //
        // Forward to implementation
        //
        String eNewID = (eSetID==null || eSetID.length()==0 ?
                eCreateID(eURI, eImpl, eImpl.eClass(), eAttribute) :
                    eUseID(eURI, eImpl, eAttribute, eSetID, true));
        //
        // If EObject is an EFeature, then set ID.
        // Otherwise, throw an IllegalArgumentException
        //
        return eInverseSet(eObject,eNewID,eSetID);
       
    }

    @Override
    public String useID(EObject eObject, String eID) {
        //
        // Get implementation (strips away EFeatureDelegates)
        //
        EObject eImpl = eImpl(eObject);
        //
        // Verify eObject
        //
        verify("eObject",eImpl,true,true,true);
        //
        // Get URI
        //
        URI eURI = eImpl.eResource().getURI();
        //
        // Verify eURI
        //
        verify("eURI",eURI,true);
        //
        // Get ID attribute
        //
        EAttribute eAttribute = eAttributeMap.get(eImpl.eClass());
        //
        // Get current value
        //
        String eSetID = (String)eImpl.eGet(eAttribute);
        //
        // Check if set
        //
        boolean eIsSet = !(eSetID==null || eSetID.length()==0);
        //
        // ----------------------------------------------------------
        //  Cache ID as used
        // ----------------------------------------------------------
        //  If ID is not set in given object, make sure that ID is
        //  unique, if not unique a unique ID is returned. If ID is
        //  set in given object, then just mark it as used and return
        //  it unchanged. See eIDMap for more information.
        //
        eID = eUseID(eURI, eImpl, eAttribute, eID, eIsSet);
        //
        // If EObject is an EFeature, then set ID.
        // Otherwise, throw an IllegalArgumentException
        //
        return eInverseSet(eObject,eID,eSetID);       
    }

    @Override
    public String disposeID(EObject eObject) {
        //
        // Get implementation (strips away EFeatureDelegates)
        //
        EObject eImpl = eImpl(eObject);
        //
        // Verify eObject
        //
        verify("eObject",eImpl,true,true,false);
//        //
//        // Get URI
//        //
//        URI eURI = eImpl.eResource().getURI();
//        //
//        // Verify URI
//        //
//        verify("eURI",eURI,true);
        //
        // Get ID attribute
        //
        EAttribute eAttribute = eAttributeMap.get(eImpl.eClass());
        //
        // Get current ID
        //
        String eID = (String)eImpl.eGet(eAttribute);
        //
        // Verify
        //
        verify("eID",eID,true);
        //
        // Forward
        //
        return eDisposeID(eImpl, eID);
    }   
   
    // -----------------------------------------------------
    //  Helper methods
    // -----------------------------------------------------
   
    protected String eGetID(URI eURI, EObject eImpl) {
        //
        // Get map
        //
        Map<EObject,Long> eIDs = eInverseIDMap.get(eURI);
        //
        // Locate ID if exists?
        //
        if(eIDs!=null) {
            Long uniqueID = eIDs.get(eImpl);
            if(uniqueID!=null) {
                return ePrefix + uniqueID;
            }
        }      
        return null;
    }   
   
    protected Long eNextID(URI eURI) {
        //
        // Get next unique ID from map
        //
        Long uID = eNextIDMap.get(eURI);
        //
        // First ID in given resource?
        //
        if(uID==null) uID = 1L;
        //
        // Finished
        //
        return uID;
    }
   
    /**
     * Ensure that ID is unique. If not, return one that is.
     * <p>
     * This method used the {@link #eInverseIDMap} to ensure that
     * the next ID is is unique
     * @param eImpl - {@link EObject} instance
     * @param eImpl - the object with ID
     * @param uID - proposed ID
     * @return actual unique ID
     */
    protected Long eUniqueID(URI eURI, EObject eImpl, Long uID) {
        //
        // Get Map of cached ID for given URI
        //
        Map<EObject,Long> eCachedIDs = eInverseIDMap.get(eURI);
        //
        // Found cached IDs?
        //
        if(eCachedIDs!=null) {           
            //
            // Check if an equal ID value is already cached for given object
            //
            if(uID.equals(eCachedIDs.get(eImpl)))
                return uID;
            //
            // Initialize upper bounds for safe exit
            //
            int size = eCachedIDs.size();
            int count = 0;
            //
            // Continue until unique is found or end of map reached
            //
            while(eCachedIDs.containsValue(uID) && count<size) {
                uID++;
                count++;
            }
        }
        //
        // Finished
        //
        return uID;
    }
   
    /**
     * Create ID from given information.
     */
    protected String eCreateID(URI eURI, EObject eImpl, EClass eClass, EAttribute eID)
        throws IllegalArgumentException {
        //
        // Initialize unique id to "unknown"
        //
        Long uID = 0L;
        //
        // Try to get unique ID as string
        //
        String sID = getID(eImpl);
        //
        // Get current ID
        //
        if( !(sID==null || sID.length()==0) ) {
            //
            // Remove prefix
            //
            sID = sID.replace(ePrefix, "");
            //
            // Convert to long
            //
            uID = Long.decode(sID);
        }
        //
        // Current ID is "unknown"?
        //
        if(uID==0L) {
            //
            // Get next ID from map
            //
            uID = eNextIDMap.get(eURI);
            //
            // Start to create IDs for new resource?
            //
            if(uID==null) {
                //
                // Set first ID in this series
                //
                uID = 1L;
            } else {
                //
                // Validate against cached IDs
                //
                uID = eUniqueID(eURI, eImpl, uID);
            }
        }
        //
        // Add to ID maps
        //
        return eCacheID(eURI,eImpl,uID);
    }   
   
    protected String eUseID(URI eURI, EObject eImpl,
            EAttribute eAttribute, String eID, boolean eIsSet) {
        //
        // Remove prefix
        //
        eID = eID.replace(ePrefix, "");
        //
        // Convert to long
        //
        Long uID = Long.decode(eID);
        //
        // Ensure that is is unique?
        //
        if(!eIsSet) {
            uID = eUniqueID(eURI, eImpl, uID);
        }
        //
        // Cache the ID as used
        //
        return eCacheID(eURI, eImpl, uID);
    }       
   
    protected String eCacheID(URI eURI, EObject eImpl, Long uID) {
        //
        // Ensure that EFeatureDelegates are not cached
        //
        if(eImpl instanceof EFeatureDelegate) {
            throw new IllegalArgumentException("IDs can not be associated with EFeatureDelegate instances");
        }       
        //
        // Get Object cached for given ID, create it if not found
        //
        Map<Long,WeakHashSet<EObject>> eCachedIDSets = eIDMap.get(eURI);
        if(eCachedIDSets==null) {
            eCachedIDSets = new HashMap<Long,WeakHashSet<EObject>>();
            eIDMap.put(eURI, eCachedIDSets);
        }
        WeakHashSet<EObject> eObjectSet = eCachedIDSets.get(uID);
        if(eObjectSet==null) {
            eObjectSet = new WeakHashSet<EObject>(EObject.class);
            eCachedIDSets.put(uID, eObjectSet);           
        }               
        //
        // Add object to hash set
        //
        eObjectSet.add(eImpl);               
        //
        // Get EObjects cached for given URI, create it if not found
        //
        WeakHashMap<EObject,Long> eInverseIDs = eInverseIDMap.get(eURI);
        if(eInverseIDs==null) {
            eInverseIDs = new WeakHashMap<EObject,Long>();
            eInverseIDMap.put(eURI, eInverseIDs);
        }       
        //
        // Add to ID cache
        //
        eInverseIDs.put(eImpl, uID);
        //
        // Update next ID?
        //
        if(eNextID(eURI)<=uID) {
            eNextIDMap.put(eURI,uID+1);
        }
        //
        // Finished
        //
        return ePrefix+uID;
    }
   
    protected String eInverseSet(EObject eObject, String eNewID, String eSetID) {
        if((eObject instanceof EFeature)) {
            if(!eNewID.equals(eSetID)) {
                if(eObject instanceof EFeatureImpl) {
                    return ((EFeatureImpl)eObject).eInternal().eSetID(eNewID, false);
                } else if(eObject instanceof EFeatureDelegate) {
                    return ((EFeatureDelegate)eObject).eInternal().eSetID(eNewID, false);
                }
                //
                // ------------------------------------------------------
                //  !!! EFeature not implemented !!!
                // ------------------------------------------------------
                //  Just as implementors or EObject are expected to
                //  implement InternalEObject, implementors of EFeature
                //  are expected to implement EFeatureInternal or
                //  EFeatureDelegate. These implementations have guards
                //  that break the recursive call sequence
                //  EFeature.setID() -> EFeatureIDFactory.useID() ->
                //  EFeature.setID(), handles the context startup
                //  problem and much more which direct implementors of
                //  EFeature must handle themselves. Try
                //
                //
                //  This will probably fail, but let's try it out anyway ...
                //
                try {
                    ((EFeature)eObject).setID(eNewID);
                } catch (Exception e) {
                    //
                    // Notify that this is a unrecoverable errors
                    //
                    String msg = "EFeature is implemented correctly. " +
                         "Extend EFeatureImpl or use a EFeatureDelegate instead. ";
                    //
                    // Log message as severe. This should give the implementor some additional hints...
                    //
                    LOGGER.log(Level.SEVERE, msg + e.getMessage(), e);
                    //
                    // Throw it again
                    //
                    throw ((IllegalArgumentException)(new IllegalArgumentException(msg).initCause(e)));
                }
            }
            return eNewID;
        }
        //
        // This should never happen...
        //
        throw new IllegalArgumentException("'" + eObject + " does not implement EFeature");
    }
   
    protected String eDisposeID(EObject eObject, String eID)
        throws IllegalArgumentException {
        //
        // Remove prefix
        //
        eID = eID.replace(ePrefix, "");
        //
        // Convert to long
        //
        Long uID = Long.decode(eID);       
        //
        // Find URI
        //
        URI eURI = null;
        for(Entry<URI,Map<Long,WeakHashSet<EObject>>> it : eIDMap.entrySet()) {
            WeakHashSet<EObject> eSet = it.getValue().get(uID);
            if(eSet!=null && eSet.contains(eObject)) {
                eURI = it.getKey();
            }
        }
        //
        // Verify that URI was found
        //
        if(eURI==null) {
            //
            // No IDs created for given resource
            //
            throw new IllegalArgumentException("Object "
                    + eObject.getClass().getSimpleName()
                    + "[" + eID + " ] not found");
        }   
        //
        // Verify that URI was
        //
        WeakHashMap<EObject,Long> eCachedIDs = eInverseIDMap.get(eURI);
        if(eCachedIDs==null) {
            //
            // No IDs created for given resource
            //
            throw new IllegalArgumentException("No IDs created for " +
                "resource [" + eURI + " ]");
        }       
        //
        // Get ID from cache
        //
        uID = eCachedIDs.get(eObject);
        //
        // ID not found?
        if(uID == null) {
            //
            // No ID created for given object
            //
            throw new IllegalArgumentException("No ID created for " + eObject);           
        }
        if(eID.equals(ePrefix+uID)) {
            //
            // Not same as ID created for given object
            //
            throw new IllegalArgumentException("Expected ID " + eID + "," +
                    "found " + ePrefix + uID);                       
        }
        //
        // Dispose ID
        //
        eCachedIDs.remove(eObject);
        //
        // Finished
        //
        return eID;
    }   
       
    protected void verify(String eType, Object object, boolean isNonNull) {
        if(isNonNull && object==null) {
            throw new NullPointerException(eType + " can not be null");
        }
       
    }
   
    protected void verify(String eType, EObject eObject, boolean isNonNull, boolean isValid, boolean isLoaded) {       
        verify(eType, eObject,isNonNull);
        if(isValid && !creates(eObject)) {
            throw new IllegalArgumentException(eType + " " + getName(eObject) + " is not valid");
        }
        if(isLoaded && eObject.eResource()==null) {
            throw new IllegalStateException(eType + " " + getName(eObject) + " is not loaded into a resource");           
        }
    }
   
    protected static String getName(EObject eObject) {
        if(eObject==null) return "null";
        if(eObject instanceof ENamedElement) {
            return ((ENamedElement)eObject).getName();
        }
        return eObject.toString();
    }
   
    protected EObject eImpl(EObject eObject) {
        if(eObject instanceof EFeatureDelegate) {
            eObject = ((EFeatureDelegate)eObject).eImpl();
        }
        return eObject;
    }

}
TOP

Related Classes of org.geotools.data.efeature.impl.EFeatureIDFactoryImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.