/*
 * $Header: /home/cvspublic/jakarta-slide/src/share/org/apache/slide/store/ResourceId.java,v 1.6 2004/11/08 09:30:16 luetzkendorf Exp $
 * $Revision: 1.6 $
 * $Date: 2004/11/08 09:30:16 $
 *
 * ====================================================================
 *
 * Copyright 1999-2002 The Apache Software Foundation 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.apache.slide.store;

import java.util.Enumeration;
import org.apache.slide.common.Namespace;
import org.apache.slide.common.Scope;
import org.apache.slide.common.ScopeTokenizer;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.ServiceConnectionFailedException;
import org.apache.slide.common.ServiceMissingOnRootNodeException;
import org.apache.slide.common.SlideRuntimeException;
import org.apache.slide.common.SlideToken;
import org.apache.slide.common.Uri;
import org.apache.slide.common.UriPath;
import org.apache.slide.content.NodeProperty;
import org.apache.slide.store.Store;
import org.apache.slide.util.Configuration;
import org.apache.slide.util.XMLValue;
import org.jdom.Element;

/**
 * Uniquely identifies a resource. The main operation with ResourceIds is equals. Immutable.
 *
 * Technically, ResourceId extends Uri, but the only reason to do so is compatibility with
 * existing store interfaces. Logically, a ResourceId is *not* an Uri: Uris have a structure,
 * resourceId have not. Consequently, almost all Uri methods in this class throw
 * UnsupportedOperation exceptions.
 *
 * @version   $Revision: 1.6 $
 */
public final class ResourceId extends Uri {
    
    public static String RESOURCE_ID_SCHEMA = "urn:uuid:";
    
    
    /**
     ** There are probably no two jvm's working on the same database that initialitzing this
     ** class at the same mill second
     **/
    private static final String TIMESTAMP =  String.valueOf(System.currentTimeMillis());
    
    /**
     ** Number of uuri created in this JVM session
     **/
    private static int counter1 = 0;
    private static int counter2 = 0;
    
    /**
     * @pre  uriStr has to contain store prefix, i.e. extractStoreUri has to succeed
     */
    public static ResourceId createNew(Uri uri) {
        String newUuri;
        boolean useBinding = Configuration.useBinding(uri.getStore());
        
        if (useBinding) {
            StringBuffer b = new StringBuffer();
            b.append(uri.getScope().toString());
            if (b.length() > 1) b.append("/");
            
            if (!uri.isStoreRoot()) {
                synchronized (ResourceId.class) { 
                    b.append(TIMESTAMP).append("/").append(counter2)
                        .append("/") .append(counter1++);
                    if (counter1 > 500) {
                        counter1 = 0;
                        counter2++;
                    }
                }
            }
            newUuri = b.toString();
        } else {
            newUuri = uri.toString();
        }
        return new ResourceId(uri, newUuri);
    }
    
    public static ResourceId create(Uri uri, String uuri) {
        return new ResourceId(uri, uuri);
    }
    
    private static String resourceIdSchema(Store store) {
        if (Configuration.useBinding(store)) {
            return RESOURCE_ID_SCHEMA;
        }
        else {
            return "";
        }
    }
    
    private final String uuri;
    
    /**
     * Constructor
     *
     * @param    uri                 an Uri
     * @param    id                  /scope/identifier
     *
     */
    private ResourceId(Uri uri, String uuri) {
        super(uri.getToken(), uri.getNamespace(), uri.toString());
        this.uuri = uuri;
        parseUuri(uuri);
    }
    
    /**
     * Tests equivalence of two ResourceIds.
     *
     * @param obj Object to test
     * @return boolean
     */
    public boolean equals(Object obj) {
        ResourceId resourceId;
        
        if (obj instanceof ResourceId) {
            resourceId = (ResourceId) obj;
            return getNamespace() == resourceId.getNamespace() &&
                getStore() == resourceId.getStore() && uuri.equals(resourceId.uuri);
        } else {
            return false;
        }
    }
    
    public String toString() {
        return uuri;
    }
    
    /**
     * Intentionally does not return anything like the origianl uri.
     *
     * @return String
     */
    public String getUuri() {
        return uuri;
    }
    
    /**
     * Hash code.
     *
     * @return int hash code
     */
    public int hashCode() {
        return this.uuri.hashCode();
    }
    
    public Scope getScope() {
        return super.getScope();
    }
    
    public Store getStore() {
        return super.getStore();
    }
    
    public SlideToken getToken() {
        return super.getToken();
    }
    
    public Namespace getNamespace() {
        return super.getNamespace();
    }
    
    public boolean isStoreRoot() {
        UriPath thisPath = new UriPath(uuri);
        UriPath scopePath = new UriPath(scope.toString());
        return thisPath.equals(scopePath);
    }
    
    // -------------------------------------------------------------
    
    public void setUri(String uri) {
        throw new UnsupportedOperationException();
    }
    
    public Enumeration getScopes() {
        throw new UnsupportedOperationException();
    }
    
    
    public void setToken(SlideToken token) {
        throw new UnsupportedOperationException();
    }
    
    public Uri getParentUri() {
        throw new UnsupportedOperationException();
    }
    
    public void invalidateServices() {
        throw new UnsupportedOperationException();
    }
    
    public void reconnectServices() {
        throw new UnsupportedOperationException();
    }
    
    public String getRelative() {
        throw new UnsupportedOperationException();
    }
    
    /**
     * Do not clone ResourceId's, use aliasing.
     */
    public Uri cloneObject() {
        throw new UnsupportedOperationException();
    }
    
    public boolean isParent(Uri uri) {
        throw new UnsupportedOperationException();
    }
    
    public String asXml() {
        XMLValue r = new XMLValue();
        Element hrefElm = new Element("href", NodeProperty.NamespaceCache.DEFAULT_NAMESPACE);
        hrefElm.setText(resourceIdSchema(getStore())+getUuri());
        r.add(hrefElm);
        return r.toString();
    }
    
    /**
     * This function is called by the constructor and when uri is changed.
     *
     * @param uri Uri to parse
     */
    protected void parseUuri(String uuri) {
        // We first try to tokenize the uri string.
        scopes = new ScopeTokenizer(token, namespace, uuri);
        
        // Find the qualifiying stuff from the registry.
        // Then we contentStore the scope of the found Data Source
        // within scope.
        store = null;
        while ((store == null) && (scopes.hasMoreElements())) {
            Scope courScope = scopes.nextScope();
            try {
                if (token == null) {
                    store = namespace.retrieveStore(courScope, null);
                } else {
                    store = namespace.retrieveStore(courScope, token.getCredentialsToken());
                }
                
                if (store != null) {
                    scope = courScope;
                }
            }
            catch (ServiceConnectionFailedException e) {
                throw new SlideRuntimeException(e.toString(), true);
            }
            catch (ServiceAccessException e) {
                throw new SlideRuntimeException(e.toString(), true);
            }
        }
        
        // If descriptorsStore or contentStore is still null, then no valid
        // scope is defined in the namespace ...
        if (store == null) {
            throw new ServiceMissingOnRootNodeException();
        }
    }
    
    /**
     * Does nothing, overwrited the {@link Uri#parseUri(String)} method to
     * avoid its unrequired overhead.
     */
    protected void parseUri(String uri) {
       
    }
    
}

