/*
 * $RCSfile: GlobalResourceRegistry.java,v $
 * $Date: 2006/04/28 15:58:59 $
 * $Revision: 1.1 $
 * $Author: xblanc $
 */
 
/*
 * Copyright (c) 2002-2003 IST-2004-2006-511731 ModelWare - ModelBus.
 * All rights reserved.
 *
 * This software is published under the terms of the ModelBus Software License
 * 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.
 * A copy of ModelBus Software License is provided with this distribution in
 * doc/LICENSE.txt file.
 */
 
/*
 * GlobalResourceRegistry.java Add description 
 * 
 * @author Prawee Sriplakich
 * @version $Revision: 1.1 $ $Date: 2006/04/28 15:58:59 $
 * @see Add references
 *
 * TODO Add description
 * TODO Add references
 */
package org.eclipse.mddi.modelbus.adapter.infrastructure.model_manipulation;

import java.util.Collection;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.apache.log4j.Logger;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.resource.impl.URIConverterImpl;

public class GlobalResourceRegistry {
    
    private static Logger logger = Logger.getLogger("GlobalResourceRegistry");
        
    /**
     * The prefixes of URIs of global resources.
     * We assume that global resources must have the prefix 
     * "pathmap://ModelBus" or "http://"
     */
    public static final List GLOBAL_URI_PREFIX_SET = new Vector();
     
    static {
        GLOBAL_URI_PREFIX_SET.add("pathmap://ModelBus");
        GLOBAL_URI_PREFIX_SET.add("http://");           
    }
    
    private URIConverter uriConverter = new URIConverterImpl();
    private Map resourceMap = new Hashtable();
    
    /**
     * Returns all resources managed by this Registry.
     * The returned collection only supports removal operation (no add).
     * 
     * @return
     * 
     */       
    public Collection getResources() {
        return resourceMap.values();
    }
    
    
    /**
     * 
     * Register a shared resource so that ModelBus can create links to it 
     * when deserializing a model referencing this resource. 
     * <br>
     * After registration, the URI of the resource will change to the speicified global URI.
     * <br>
     * If, before registration, the resource is associated to a local URI 
     * (e.g. file URI), this URI will become the Alias to the global URI.
     * <br>
     * If the current URI of the resource has global scheme,
     * you can not register the resource with another URI. 
     * Otherwise IllegalArgumentException will the thrown.
     * <br>
     * A resource can be registerred only once.
     * 
     * 
     * @param globalURI The gobal URI must have the scheme "pathmap" or "http" 
     * (i.e. "pathmap://....." or "http://..")
     * @param r
     * @throws IllegalArgumentException
     */
    public void registerResource(URI globalURI, Resource r) throws GlobalResourceException {
        if(!hasGlobalURIScheme(globalURI) ) {
            throw new GlobalResourceException("Global URI must have scheme 'pathmap' or 'http'" );               
        }
        if( hasGlobalURIScheme(r.getURI()) && !globalURI.equals(r.getURI())) {
            throw new GlobalResourceException("Resource already associated to " + r.getURI() );               
        }
        if(resourceMap.containsKey(globalURI)) {
            Resource boundResource = (Resource) resourceMap.get(globalURI);
            if(boundResource==r ) {
              logger.debug("Duplicated registration (ignored): " + globalURI.toString());
              return;
            } 
            throw new GlobalResourceException("Conflict registration: " 
                    +globalURI +" is already bound to " +boundResource 
                    +". Cannot rebind to "+ r );
        }
        if(resourceMap.containsValue(r)) {
            throw new GlobalResourceException("Conflict registration: " 
                    +r +" is already registered with a different URI."
                    +" Cannot rebind to "+ globalURI );
        }      
        resourceMap.put(globalURI, r);
        
        logger.debug("register uri: " +globalURI);
        
        // Set the Resource URI to the global one.
        // The global URI needs to be used when ModelBus serializes this resource.
        if(!globalURI.equals(r.getURI())) {
           uriConverter.getURIMap().put(r.getURI(), globalURI); 
           r.setURI(globalURI);
        }
    }
    
    
    /**
     * 
     * Find a registerred resource
     * 
     * @param uri This URI can be either the global URI of the resource or the alias URI.
     * @return
     * 
     */
    public Resource getResource(URI uri) {
        // If this uri is global URI
        // check whether the corresponding resource exist if yes -> return the resouce
        Resource r = null;
        if(hasGlobalURIScheme(uri)) {
            r = (Resource) resourceMap.get(uri);
            if(r!=null) return r;
        }
        // resolve the alias URI to global URI
        URI normalizedURI = uriConverter.normalize(uri);
        r = (Resource) resourceMap.get(normalizedURI);
        if(r==null && hasGlobalURIScheme(normalizedURI)) {
             logger.debug("resolve uri: " +uri +" -> " +r);
        }           
        return r;
    }  
    
    
    /**
     * 
     * URIConverter allows Resource aliasing.
     * <br>
     * It enables you to avoid multiple reloading of the same model 
     * that is stored in different copies (with different file names). 
     * <br><br>
     * For example, the XMI file of model M1 references SharedModel1 with the file URI "File1".
     * The XMI file of model M2 also references another copy of SharedModel1 with the file URI "File2".
     * You should set aliases File1->SharedModel1_global_URI and File2->SharedModel1_global_URI.
     * Consequently, when you ask ModelBus to load M1 and M2 (using ModelBusResourceSet),
     * ModelBus will resolve the differnet file URIs (M1->SharedModel1) (M2-SharedModel1) to the same SharedModel1 objects.
     * Note that, without aliasing, SharedModel1 will be loaded twice 
     * (once for resolving the reference from M1 and another time for M2)
     * <br><br>
     * To set the alias URI, uses getURIConverter().getURIMap().put(aliasURI, globalURI);
     * 
     * 
     */
     public URIConverter getURIConverter() {
        return uriConverter;
     }
     
     /**
      * 
      * Check whether this URI has the global scheme (pathmap or http)
      */
      public static boolean hasGlobalURIScheme(URI uri) {
         return ModelUtil.uriBeginsWith(uri, GLOBAL_URI_PREFIX_SET);
      }
      
      public static class GlobalResourceException extends Exception {
          /**
           * 
           */
          private static final long serialVersionUID = -4580625527117814989L;

          public GlobalResourceException(String message) {
              super(message);
          } 
      }
 } 