Package com.CompPad.OOO

Source Code of com.CompPad.OOO.OOODocument

/* Copyright 2011 Toby D. Rule

  This file is part of CompPad, an OpenOffice extension to provide live
  mathematical and engineering calculations within a Writer document.

    CompPad is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    CompPad 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with CompPad.  If not, see <http://www.gnu.org/licenses/>.
*/



package com.CompPad.OOO;

import com.CompPad.model.Document;
import com.CompPad.model.Listener;
import com.sun.star.beans.XPropertySet;
import com.sun.star.container.XEnumeration;
import com.sun.star.container.XEnumerationAccess;
import com.sun.star.container.XIndexAccess;
import com.sun.star.container.XNameAccess;
import com.sun.star.document.XEmbeddedObjectSupplier;
import com.sun.star.frame.XModel;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.text.XDependentTextField;
import com.sun.star.text.XTextDocument;
import com.sun.star.text.XTextEmbeddedObjectsSupplier;
import com.sun.star.text.XTextField;
import com.sun.star.text.XTextFieldsSupplier;
import com.sun.star.uno.Any;
import com.sun.star.uno.Exception;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.util.XRefreshable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Manage openoffice elements of the comppad document separately from the
* CompPad elements,
* @author brenda
*/
public class OOODocument {
    private Integer nResult;
    private Integer nError;
    /* Case in CLSID strings is inconsistent - do we need to use
     * case-insensitive comparison just to be safe? */
    public static String CLSID_FORMULA="078B7ABA-54FC-457F-8551-6147e776a997";
    public static String CLSID_CHART="12DCAE26-281F-416F-a234-c3086127382e";
    private XTextDocument xTextDocument;
//    Document compPadDocument;
    /* This is a list of CompPad element in OOO Document */
    private LinkedList<OOOElement> OOOElementList = new LinkedList();
    private XMultiServiceFactory xMSF;
    private Document document;
    /* Create single listener to listen to many expressions. Must be inner class
     in order to access private document fields and methods. */
    public LinkedList <OOOElement>elementTypes = new LinkedList();
    Hashtable<String, ArrayList<XEmbeddedObjectSupplier>> embeddedObjectHashtable = new Hashtable();

    public OOODocument(XTextDocument xTextDocArg) throws java.lang.Exception{
        Logger.getLogger("com.CompPad").log(Level.FINE,"Constructing OOODocument, \n    xTextDocArg = "
                + xTextDocArg);

        elementTypes.add(new OOOError());
        elementTypes.add(new OOOPlot());
        elementTypes.add(new OOOResult());

        // Get xMultiServiceFactory interface for document

        /* initialize the CompPad Document object */
        nResult=0;
        nError=0;
        document = new Document();


        /* get document url, and if it's file:// then strip this and the file name and
         * set that as the document working directory */
        String docURL = ((XModel)UnoRuntime.queryInterface(XModel.class, xTextDocArg)).getURL();
        if (docURL.contains("file://")){
            String wdir = docURL = docURL.replaceFirst("file://", "").replaceFirst("/[^/]+$", "") + "/";
            document.setWorkingDir(wdir);
        }

        xTextDocument = xTextDocArg;
        xMSF = (XMultiServiceFactory) UnoRuntime.queryInterface(
                XMultiServiceFactory.class, xTextDocument);
        // Constructor will find all the existing formulas and add them
        // to the object.
        /* Locate formulas in document and create corresponding OOOExpression
         * object for each one. */
       
        /* We need to index OOOElements according to the identity
         of the openoffice objects!  Otherwise, we will create new OOOExpression
         objects that are duplicates. */

        /* Find text fields and link to formulas */
        /* It shouldn't do getEmbeddedObjects twice!  This is slow */


        Logger.getLogger("com.CompPad").log(Level.FINE,"Finding embedded objects");
        ArrayList<XEmbeddedObjectSupplier> eos;
        Logger.getLogger("com.CompPad").fine("created eos");
        eos = getEmbeddedObjects(CLSID_FORMULA);
        Logger.getLogger("com.CompPad").fine("got eos");
        for (XEmbeddedObjectSupplier xEO:eos){
            // Add expression to expression list
            /* NEED TO CHANGE SO ELEMENTS ARE INDEXED BY THEIR OPENOFFICE
             * OBJECTS */
            Logger.getLogger("com.CompPad").log(Level.FINE,"  Adding OOOExpression to OOOElementList");
            OOOElementList.add(new OOOExpression(this,xEO));
        }
        for (XEmbeddedObjectSupplier xEO:getEmbeddedObjects(CLSID_CHART)){
            System.err.println("  Adding OOOPlot to OOOElementList");
            OOOElementList.add(new OOOPlot(this,xEO));
        }

        for (XDependentTextField xDTF : getTextFields()){
            String name = (String) xDTF.getTextFieldMaster().getPropertyValue("Name");

             /* NEED TO INDEX BY THE OOO Object!! */
             if (name.startsWith("    OOOResult")){
                /* create OOOResult object and add to OOOElementList*/
                 OOOElementList.add(new OOOResult(this,xDTF));
             }
             else if (name.startsWith("    OOOError")){
                /* Create OOOError object and add to OOOElementList */
                OOOElementList.add(new OOOError(this,xDTF));
             }
        }
        /* Sort OOOExpressions so their order in expressionList corresponds to order
         * in document.  This is needed in FixTextFields */
        /* This will throw an error if OOOElements is empty */
        Collections.sort(OOOElementList);

        /* Now link OOOError and OOOResult elements to OOOExpressions according
         * to their order in the document.  When expressions are evaluated,
         * these may be later created or deleted, according to results of listener.
         * If expression says "no result" then the result will be deleted.  If
         * expression says "result" then result will be created or updated.
         * If expression says "error" then error will be created or updated.
         * */
        OOOExpression currentExpression=null;
        for (OOOElement oooElement:OOOElementList){
            if (OOOExpression.class.isInstance(oooElement)){
                /* Set this as the "current" expression, and link subsequent results or
                 * errors or plots or whatever to this expression. */
                currentExpression=(OOOExpression)oooElement;
            }
            else{
                /* Will throw error if null */
                /* Only last dependant element of each class gets kept */
                currentExpression.setDependent(oooElement);
            }
        }


        /* At this point, everythings been added to the document, and the
         * OOOExpression objects should have taken care of creating the
         * expressions.
         *
         * We will pass a comparable interface from the OOOExpression to the
         * expression so that the expressions can sort themselves.
        */

        /* Fix text fields that no longer have matching expression, or that
         * are not positioned correctly */
         /* I would rather not do this until I actually evaluate the expression */
        fixTextFields();


            /*  */

        /* Add refresh listener */
        System.out.println("Adding refresh listener");
        document.setRefreshListener(new RefreshListener());

        /* Evaluate document */
        System.out.println("Evaluating document");
        document.evaluate();
    }
   

    public XTextDocument getTextDocument(){
        // Returns the XTextDocument interface for the document containing compPAd
        return xTextDocument;
    }

   
    /** 
     * get list of XEmbeddedObjectSuppliers for embedded math objects
     * @return
     * @throws java.lang.Exception
     */
    private ArrayList<XEmbeddedObjectSupplier>
            getEmbeddedObjects(String argCLSID) throws java.lang.Exception {
        ArrayList<XEmbeddedObjectSupplier> xEmbeddedObs= new ArrayList();
        Logger.getLogger("com.CompPad").fine("getEmbeddedObjects 00");
        /* !!!! It would be better to make a hashtable with CLSID as key,
         * and with the members being linked lists filled with the XEOS's !!!! */

        /* Test enumeration access - should be faster if it works ! */
        /* For now, check if hashtable is zero length.  In future, may want to
         * re-check hashtable if new objects have been added.  This is a slow
         * loop, so only do it once */
        if (embeddedObjectHashtable.size()==0){
            Logger.getLogger("com.CompPad").fine("getEmbeddedObjects 02");
            XEmbeddedObjectSupplier xEOS;
            XTextEmbeddedObjectsSupplier xEOsS =
                (XTextEmbeddedObjectsSupplier) UnoRuntime.queryInterface(
                    com.sun.star.text.XTextEmbeddedObjectsSupplier.class,
                    xTextDocument);
            Logger.getLogger("com.CompPad").fine("getEmbeddedObjects 05");
            XIndexAccess indexEmbeddedObjects =
                    (XIndexAccess) UnoRuntime.queryInterface(
                    XIndexAccess.class,(xEOsS.getEmbeddedObjects()));
            Logger.getLogger("com.CompPad").fine("getEmbeddedObjects 10");
            for (int i =0; i<indexEmbeddedObjects.getCount();i++){
                Logger.getLogger("com.CompPad").fine("getEmbeddedObjects20");
                // EmbeddedObjects.getByName() returns an Any object, which has the
                // type embedded with the object.s
                xEOS= (XEmbeddedObjectSupplier)
                        ((Any) indexEmbeddedObjects.getByIndex(i)).getObject();

                /* The following three lines appear to have been causing slowdown.
                 * And also, they are not needed! */

                //Object xEO=xEOS.getEmbeddedObject();
                //XPropertySet xPSxEO=(XPropertySet)
                //        UnoRuntime.queryInterface(XPropertySet.class, xEO);

                // The service TextEmbeddedObject that provides the XEOS interface
                // also provides the CLSID property
                XPropertySet xPSxEOS=(XPropertySet)
                        UnoRuntime.queryInterface(XPropertySet.class,xEOS);
                // Get CLSID value
                String clsid=(String) xPSxEOS.getPropertyValue("CLSID");
                if (!embeddedObjectHashtable.containsKey(clsid)){
                    embeddedObjectHashtable.put(clsid, new ArrayList<XEmbeddedObjectSupplier>());
                }
                embeddedObjectHashtable.get(clsid).add(xEOS);


            }
        }
        Logger.getLogger("com.CompPad").fine("getEmbeddedObjects 30");
        /* Return empty list rather than null */
        if (!embeddedObjectHashtable.containsKey(argCLSID)){
            return new ArrayList<XEmbeddedObjectSupplier>();
        }
        else{
            return embeddedObjectHashtable.get(argCLSID);

        }
    }

    /**
     * Find text fields, create OOOResult or OOOError, and add to list of
     * OOOElements. Don't worry now if there are field masters with multiple
     * dependent fields.
     */
    private void findTextFields () throws java.lang.Exception {
        XTextFieldsSupplier xTFS = (XTextFieldsSupplier)
                 UnoRuntime.queryInterface(XTextFieldsSupplier.class,xTextDocument);
         List docFieldMasterNames =
                 Arrays.asList((xTFS.getTextFieldMasters()).getElementNames());
         /* Create either OOOREsult or OOOError and add to list of OOOElements */
         XEnumeration xEnum = xTFS.getTextFields().createEnumeration();
         while (xEnum.hasMoreElements()){
             XDependentTextField xTF = (XDependentTextField)xEnum.nextElement();
             String name = (String) xTF.getTextFieldMaster().getPropertyValue("Name");


             /* NEED TO INDEX BY THE OOO Object */

             if (name.startsWith("com.sun.star.text.fieldmaster.User.OOOResult")){
                /* create OOOResult object and add to OOOElementList*/
                 OOOElementList.add(new OOOResult(this,xTF));
             }
             else if (name.startsWith("com.sun.star.text.fieldmaster.User.OOOError")){
                /* Create OOOError object and add to OOOElementList */
                OOOElementList.add(new OOOError(this,xTF));
             }

         }
        
    }

    private ArrayList<XDependentTextField> getTextFields () throws java.lang.Exception {
        XTextFieldsSupplier xTFS = (XTextFieldsSupplier)
                 UnoRuntime.queryInterface(XTextFieldsSupplier.class,xTextDocument);
        ArrayList<XDependentTextField> xDTF=new ArrayList();
         /* Create either OOOREsult or OOOError and add to list of OOOElements */
        XEnumeration xEnum = xTFS.getTextFields().createEnumeration();
        XTextField xTF;
        while (xEnum.hasMoreElements()){
//            xDTF.add( (XDependentTextField) xEnum.nextElement())) ;
//            Logger.getLogger("com.CompPad").log(Level.FINE,"nextelement: "+((XTextField)((Any)xEnum.nextElement()).getObject()).getPresentation(true));
           
            xTF= (XTextField)((Any)xEnum.nextElement()).getObject();

            xDTF.add((XDependentTextField)UnoRuntime.queryInterface(
                    XDependentTextField.class,xTF));
        }
        return xDTF;
    }

    private void fixTextFields() throws java.lang.Exception {
        Logger.getLogger("com.CompPad").log(Level.FINE,"Fixing Text Fields");
        String name;
        XPropertySet fieldMaster;
        LinkedList expFieldMasterNames=new LinkedList();
         XTextFieldsSupplier xTFS = (XTextFieldsSupplier)
                 UnoRuntime.queryInterface(XTextFieldsSupplier.class,xTextDocument);
         List docFieldMasterNames =
                 Arrays.asList((xTFS.getTextFieldMasters()).getElementNames());
         String expName;
         for (Object item:docFieldMasterNames){
             name = (String)item;
             /* The strings in element list don't contain the prefix, so
              prefix must be removed from docFieldMasterName before checking it */
             expName=name.replace("com.sun.star.text.fieldmaster.User.", "");
             if (name.startsWith("com.sun.star.text.fieldmaster.User.OOO") & !this.hasElementName(expName)){
                  Logger.getLogger("com.CompPad").log(Level.FINE,"    orphan:"+ expName);
                  /* the field master is orphaned. Remove the field master and
                   * dependent fields */
                  fieldMaster = (XPropertySet)(((Any)
                          ((xTFS.getTextFieldMasters()).getByName(name))).getObject());
                  /* Dispose of fieldmaster */
                  ((XComponent)UnoRuntime.queryInterface(
                          XComponent.class, fieldMaster)).dispose();
                  /* When the fieldmaster is disposed of, the
                   * dependent text fields are disposed of as well, since they
                   * can't exist without a master */
              }
         }

    }
    /**
     * get the associated comppad document
     * @return
     */
    Document getCompPadDocument(){
        return document;
    }


    public XPropertySet newFieldMaster(String name) throws java.lang.Exception{
            XPropertySet outputFieldMaster;
            Object oFM;

            Logger.getLogger("com.CompPad").log(Level.FINE,"newFieldMaster: "+name);

            try{
                /* Should work in OO 3 */
                oFM = (Object) xMSF.createInstance("com.sun.star.text.fieldmaster.User");
            }
            catch(com.sun.star.uno.Exception exception){
                /* Should work in OO 2 */
                oFM = (Object) xMSF.createInstance("com.sun.star.text.FieldMaster.User");
            }
            // There is not field master interface, just a bunch of properties
            outputFieldMaster = (XPropertySet) UnoRuntime.queryInterface(XPropertySet.class, oFM);
            outputFieldMaster.setPropertyValue("Name", name);
            /* I think I had to change this for OpenOffice 2.0.  I expect
             * I'll have to check the type of the object before accessing it. */
//            Logger.getLogger("com.CompPad").log(Level.FINE,"Created OutputFieldMaster "
//                    + (String)((Any)(outputFieldMaster.getPropertyValue("Name"))).getObject());
              Logger.getLogger("com.CompPad").log(Level.FINE,"Created OutputFieldMaster "
                    + outputFieldMaster.getPropertyValue("Name"));

           return outputFieldMaster;
    }

    public XDependentTextField createField(XPropertySet outputFieldMaster) throws Exception{
        /* Create fields, since they either didn't exist, or were removed */
        /* Found online that these may be mis-spelled in openoffice 2 ! */
        Object oTF;
        try{
            /* This should work in OO 3 */
            oTF = (Object) xMSF.createInstance("com.sun.star.text.textfield.User");
        }
        catch(com.sun.star.uno.Exception exception){
            /* this should work in OO 2 */
            oTF = (Object) xMSF.createInstance("com.sun.star.text.TextField.User");
        }
        XDependentTextField xTextField = (XDependentTextField)
                UnoRuntime.queryInterface(XDependentTextField.class, oTF);
        /* Attach to fieldmaster */
        xTextField.attachTextFieldMaster(outputFieldMaster);
        return xTextField;
    }

    /* not used?? */
    public XPropertySet findFieldMaster(String name) throws Exception {
        // See if there is a user field master called "CompPad"+name
        XPropertySet outputFieldMaster;
        XTextFieldsSupplier xTFS = (XTextFieldsSupplier)
                UnoRuntime.queryInterface(XTextFieldsSupplier.class, xTextDocument);
        XNameAccess xTFMs = xTFS.getTextFieldMasters();
        try {
            Logger.getLogger("com.CompPad").log(Level.FINE,"Getting fieldmaster for com.sun.star.text.fieldmaster.User.CompPad" + name);
        // OO 2 mis-spells fieldmaster as FieldMaster
        try{
        /* Should work in OO 3 */
            outputFieldMaster = (XPropertySet) ((Any) xTFMs.getByName(
                    "com.sun.star.text.fieldmaster.User.CompPad" + name)).getObject();
            Logger.getLogger("com.CompPad").log(Level.FINE,"Field exists.");
            return outputFieldMaster;
        }
        /* Should work in OO 2 */
        catch (com.sun.star.uno.Exception exception){
            outputFieldMaster = (XPropertySet) ((Any) xTFMs.getByName(
                    "com.sun.star.text.FieldMaster.User.CompPad" + name)).getObject();
            Logger.getLogger("com.CompPad").log(Level.FINE,"Field exists.");
            return outputFieldMaster;
        }
        } catch (com.sun.star.container.NoSuchElementException e) {
            Logger.getLogger("com.CompPad").log(Level.FINE,"Unable to get field master.");
            return null;
        }
    }

    /* Maybe we should create an expression listener inner class and
     * send it to all the expressions, so that they can tell the document
     * to add a new error, plot, whatever.
     */

    public String newResultName() throws java.lang.Exception {
        /* Provide a unique result name */
        String errorName = "OOOResult"+nResult;

        while (this.hasElementName(errorName)){
            nResult=nResult+1;
            errorName="OOOResult"+nResult;
        }
        nResult=nResult+1;
        return errorName;
    }
    public String newErrorName() throws java.lang.Exception {
        /* Provide a unique result name */
        String errorName = "OOOError"+nError;

        while (this.hasElementName(errorName)){
            nError=nError+1;
            errorName="OOOError"+nError;
        }
        nError=nError+1;
        return errorName;

    }

    private boolean hasElementName(String elementName) throws java.lang.Exception {
        boolean retval = false;
        for (OOOElement element: OOOElementList) {
            if (elementName.equals(element.getName())){
                retval=true;
                break;
            }
        }
        return retval;
    }

    private void removeElement(OOOElement e){

    }
    private class RefreshListener implements Listener{

        public void refresh() {
            System.out.println("Refreshing Document");
            /* Refresh all text fields */
            XTextFieldsSupplier xTFS = (XTextFieldsSupplier)
                    UnoRuntime.queryInterface(XTextFieldsSupplier.class, xTextDocument);
            XEnumerationAccess xEA = xTFS.getTextFields();
            /* Get XRefreshable interface */
            XRefreshable xRef= (XRefreshable)
                    UnoRuntime.queryInterface(XRefreshable.class,xEA);
            /* Refresh fields */
            xRef.refresh();

        }

        public void setValue(Object o)  {
            throw new UnsupportedOperationException("Not supported yet.");
        }
        public Object getValue(){
            throw new UnsupportedOperationException("Not supported yet.");
        }
    }
}
TOP

Related Classes of com.CompPad.OOO.OOODocument

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.