/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/datastore/XDescriptorsAccessor.java,v 1.3 2005/01/19 15:19:52 pnever Exp $
 * $Revision: 1.3 $
 * $Date: 2005/01/19 15:19:52 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 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.tamino.datastore;

import com.softwareag.tamino.db.api.accessor.TDeleteException;
import com.softwareag.tamino.db.api.accessor.TQuery;
import com.softwareag.tamino.db.api.accessor.TQueryBuilder;
import com.softwareag.tamino.db.api.accessor.TStreamAccessor;
import com.softwareag.tamino.db.api.accessor.TXMLObjectAccessor;
import com.softwareag.tamino.db.api.common.TException;
import com.softwareag.tamino.db.api.io.TInputStream;
import com.softwareag.tamino.db.api.objectModel.TNonXMLObject;
import com.softwareag.tamino.db.api.objectModel.TXMLObject;
import com.softwareag.tamino.db.api.objectModel.jdom.TJDOMObjectModel;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.slide.store.tamino.common.XDatastoreException;
import org.apache.slide.store.tamino.common.XGlobals;
import org.apache.slide.store.tamino.datastore.XPathExpression;
import org.apache.slide.store.tamino.datastore.XPathFactory;
import org.apache.slide.store.tamino.jdomobjects.XDescriptors;
import org.apache.slide.store.tamino.jdomobjects.XFactory;
import org.apache.slide.store.tamino.jdomobjects.XJdom;
import org.apache.slide.store.tamino.jdomobjects.XUuri;
import org.apache.slide.store.tamino.jdomobjects.XmlStream;
import org.apache.slide.store.ResourceId;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.util.XException;
import org.apache.slide.util.XUri;
import org.jdom.Element;

/**
 * @version $Revision: 1.3 $
 */
public class XDescriptorsAccessor extends XAccessor {
    public static XDescriptorsAccessor create(
        XConnection connection, String contentCollection, String tsdLanguage) {
        TXMLObjectAccessor acc;
        TStreamAccessor streamAcc;
        
        acc = connection.newXMLObjectAccessor(XGlobals.META_COLLECTION, TJDOMObjectModel.getInstance());
        streamAcc = connection.newStreamAccessor(XGlobals.META_COLLECTION);
        return new XDescriptorsAccessor(acc, streamAcc, contentCollection, tsdLanguage);
    }
    
    //--
    
    private final TStreamAccessor streamAcc;
    private final String tsdLanguage;
    private final String contentCollection;
    
    private XDescriptorsAccessor(TXMLObjectAccessor accessor, TStreamAccessor streamAcc,
                                 String contentCollection, String tsdLanguage)  {
        super(accessor);
        this.streamAcc = streamAcc;
        this.contentCollection = contentCollection;
        this.tsdLanguage = tsdLanguage;
    }
    
    public String getSchemaVersion() throws XException {
        Element el = null;
        try {
            el = doReadDescriptorsDocument("/"+contentCollection+"/");
        } catch (XException e) {}
        
        if (el == null) {
            try {
                el = doReadDescriptorsDocument(ResourceId.RESOURCE_ID_SCHEMA+"/"+contentCollection+"/");
            } catch (XException e) {}
        }
        
        return (el != null
                    ? el.getAttributeValue(XJdom.A_DTD_VERSION)
                    : XGlobals.SCHEMA_VERSION);
    }
    
    //-- crud, without read/query
    
    // TODO: dump, use insertStream
    public void insert(XDescriptors desc) throws XDatastoreException {
        insertXml(desc);
    }
    
    private void insertStream(XDescriptors desc) throws XDatastoreException {
        String doc;
        TInputStream tis;
        
        doc = getString(desc);
        try {
            tis = streamAcc.insert(TNonXMLObject.newInstance(new StringReader(doc)));
            desc.setDescriptorsId(tis.getFirstObjectId());
        } catch (TException e) {
            // e.printStackTrace();
            throw new XDatastoreException(e);
        }
    }
    
    // TODO: dump
    private void insertXml(XDescriptors desc) throws XDatastoreException {
        TXMLObject xmlObject = getXml(desc);
        try {
            response(accessor.insert (xmlObject));
            desc.setDescriptorsId(XUtilDbSession.getInoId((Element) xmlObject.getElement()));
        } catch (TException e) {
            // e.printStackTrace();
            throw new XDatastoreException(e);
        }
    }
    
    // TODO: dump, use insertStream
    public void update(XDescriptors desc) throws XDatastoreException {
        updateXml(desc);
    }
    
    private void updateStream(XDescriptors desc) throws XDatastoreException {
        String doc;
        TInputStream tis;
        
        doc = getString(desc);
        try {
            tis = streamAcc.insert(TNonXMLObject.newInstance(new java.io.StringReader(doc)));
            System.out.println(tis.getReturnValue()+","+tis.getFirstObjectId());
        } catch (TException e) {
            throw new XDatastoreException(e);
        }
    }
    
    // TODO: dump
    private void updateXml(XDescriptors desc) throws XDatastoreException {
        TXMLObject xmlObject = getXml(desc);
        try {
            response(accessor.update (xmlObject));
        } catch (TException e) {
            throw new XDatastoreException(e);
        }
    }
    
    public void delete(XDescriptors desc) throws XDatastoreException {
        try {
            response(accessor.delete(getXml(desc)));
        } catch (TException e) {
            throw new XDatastoreException(e);
        }
    }
    
    public void deleteById(String inoId) throws XException {
        TQuery xQuery = TQueryBuilder.getInstance().buildFromId (contentCollection, inoId);
        try {
            response(accessor.delete(xQuery));
        } catch (TException e) {
            throw new XDatastoreException ("cannot delete descriptors: " + xQuery, e);
        }
    }
    
    public void deleteByQuery(String queryString) throws TException, XException {
        TQuery xQuery = TQuery.newInstance(queryString);
        try {
            response(accessor.delete(xQuery));
        } catch (TDeleteException e) {
            // silently ignored because Tamino throws an exception if no documents are found
        }
    }
    
    //-- query descriptors
    
    public XDescriptors readOneRwDescriptorsByUuri(String uuri) throws XDatastoreException {
        XDescriptors desc;
        
        desc = readDescriptorsByUuri(uuri);
        if (desc == null) {
            throw new XDatastoreException("Unable to load descriptor for UURI " + uuri );
        }
        desc.setReadOnly(false);
        return desc;
    }
    
    /** @return null if not found **/
    public XDescriptors readDescriptorsByUuri(String uuri) throws XDatastoreException {
        return doReadDescriptors(XPathFactory.descByUuri(contentCollection, uuri));
    }
    
    /** @return null if not found **/
    public XDescriptors readDescriptorsByIno(String inoId) throws XException {
        return doReadDescriptors(XPathFactory.descByInoId(contentCollection, inoId));
    }
    
    public XDescriptors readDescriptorsByPath(String uri, boolean useBinding) throws XException {
        if (!useBinding) {
            return readDescriptorsByUuri(uri);
        }
        
        // resolve path
        XUri xUri = new XUri(uri);
        XUri storeUri = XUuri.extractStoreUri(xUri);
        XUri xPath = xUri.suburi(storeUri.size());
        String storeUuri = XUuri.getStoreUuri(useBinding, storeUri);
        
        XUri currentUri = storeUri;
        String currentUuri = storeUuri;
        XDescriptors currentDesc = readDescriptorsByUuri(currentUuri);
        ObjectNode currentNode = currentDesc.getObjectNode(currentUri.toString());
        for (int i = 0; i < xPath.size(); i++) {
            currentUuri = currentNode.getBindingUuri(xPath.segment(i));
            currentUri = currentUri.append(xPath.segment(i));
            currentDesc = readDescriptorsByUuri(currentUuri);
            currentNode = currentDesc.getObjectNode(currentUri.toString());
        }
        
        // TODO: exception handling
        
        return currentDesc;
    }
    
    /**
     ** @return null if not found
     **/
    private XDescriptors doReadDescriptors(XPathExpression xPath) throws XDatastoreException {
        Element el;
        
        el = queryOptElement(xPath);
        if (el == null) {
            return null;
        } else {
            return XFactory.loadDescriptor(el);
        }
    }
    
    /**
     * @param uri is the uri attribute in the descriptors, i.e. it's an uri in 0.x descriptrs,
     * and an uuri otherwise
     */
    public Element doReadDescriptorsDocument(String uri) throws XException {
        return queryOneElement(XPathFactory.descByUuri(contentCollection, uri));
    }
    
    //--
    
    public Map getUurisMap(XPathExpression xPathExpr) throws XDatastoreException {
        Map result;
        Iterator iter;
        Element el;
        
        result = new HashMap();
        iter = queryElements(xPathExpr).iterator();
        while (iter.hasNext()) {
            el = (Element) iter.next();
            // note: for historical reasons, the uuri is stored in an "uri" attribute
            result.put( XUtilDbSession.getInoId(el), el.getAttributeValue( "uri" ) );
        }
        return result;
    }
    
    private TXMLObject getXml(XDescriptors desc) {
        Element root = desc.toXml(tsdLanguage).getRootElement();
        return TXMLObject.newInstance(root);
    }
    
    private String getString(XDescriptors desc) {
        XmlStream xs;
        
        xs = new XmlStream();
        desc.toStream(xs, tsdLanguage);
        return xs.toString();
    }
}

