/* 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.Utility;
import com.CompPad.model.Listener;
import com.CompPad.model.Expression2;
import com.sun.star.beans.XPropertySet;
import com.sun.star.container.XNameAccess;
import com.sun.star.container.XNamed;
import com.sun.star.document.XEmbeddedObjectSupplier;
import com.sun.star.lang.XComponent;
import com.sun.star.text.XSimpleText;
import com.sun.star.text.XTextContent;
import com.sun.star.text.XTextCursor;
import com.sun.star.text.XTextField;
import com.sun.star.text.XTextRange;
import com.sun.star.uno.UnoRuntime;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.star.text.XTextEmbeddedObjectsSupplier;
/**
* Wrap openoffice expression objects. Create one of these objects for each
* formula in openoffice document.
* @author brenda
*/
public class OOOExpression extends OOOElement {
private OOOExpression oooExpression;
private OOOExpression thisx;
private XEmbeddedObjectSupplier xEOS;
XPropertySet xPSxEO;
// text field where output is stored
private XTextField xOutputField;
private OOODocument oooDocument; /* containing document */
private String name;
private Expression2 expression;
private OOOElement dependentElement;
private OOOExpressionListener listener = new OOOExpressionListener();
private OOOExpression xthis=this; /* make visible to inner classes */
private Class[] valueTypes={com.CompPad.model.DisplayValue.class};
/* list of element classes */
public OOOExpression(){
/* No argument constructor */
}
public OOOExpression(OOODocument oooDocArg, XEmbeddedObjectSupplier xEOS)
throws java.lang.Exception {
// formula is passed to the class as an XEmbeddedObjectSupplier
/*
* This method should probably be split up into an expression Constructor,
* which will create the object and set up its properties, and an
* Evaluator, which will create the JavaString, update the output field, etc.
*/
/* set the expression to be a dependent element of itself */
this.setDependent(this);
this.xEOS = xEOS;
thisx = this; /* So I can get this in inner classes */
// compPad object which contains the expression
oooDocument = oooDocArg;
// Get document
xTextDocument = oooDocument.getTextDocument();
XNamed xNxEOS = (XNamed) UnoRuntime.queryInterface(XNamed.class, xEOS);
// Get name of object and use as name for expression
name = xNxEOS.getName();
Logger.getLogger("com.CompPad").log(Level.FINE," Creating OOOExpression from embedded object - " + name);
/* Create new expression and add to CompPad Document.
Need to pass information to make this Comparable!!! */
expression = new Expression2(this.getFormulaString(),
this.oooDocument.getCompPadDocument().getOutputListener(), this);
oooDocument.getCompPadDocument().addExpression(expression);
/* Add listener to expression */
// expression.setResultListener (oooResult.listener);
// expression.setErrorListener(oooError.listener);
/* Add Expression listener. Then create results, plots, etc.
* as necessary */
expression.addListener(this.listener);
}
@Override
public void dispose() throws Exception{
Logger.getLogger("com.CompPad").log(Level.FINE,
"OOOExpression.dispose() called for "+this);
// xEOS.getEmbeddedObject().dispose();
}
@Override
public XTextRange getTextRange(){
XTextContent xTCxEOS = (XTextContent)
UnoRuntime.queryInterface(XTextContent.class, xEOS);
XTextRange xTextRange = xTCxEOS.getAnchor();
return xTextRange;
}
public XTextCursor getTextCursor(){
// Text anchor associated with the formula
XTextContent xTCxEOS = (XTextContent)
UnoRuntime.queryInterface(XTextContent.class, xEOS);
return xTCxEOS.getAnchor().getText()
.createTextCursorByRange(xTCxEOS.getAnchor().getEnd());
}
/**
* set dependent error field. Should be called only when document is
* initialized - might be better to pass a listener to the document to use
* for accessing the
*/
public void setDependent (OOOElement arg) throws Exception{
/* Dispose of existing element */
if (dependentElement!=null){
// dependentElement.dispose();
}
dependentElement=arg;
dependentElement.setDependsOn(this);
}
@Override
public void setDependsOn(OOOExpression arg) throws Exception{
oooExpression=arg;
}
/**
* Get the result object for this expression. Returns null if none exists
* @return
*/
public String getFormulaString() throws java.lang.Exception{
// Get embedded object
// Get xPropertySet interface for embedded object
XComponent xc = xEOS.getEmbeddedObject();
// XPropertySet xPSxEO =
xPSxEO=
(XPropertySet) UnoRuntime
.queryInterface(XPropertySet.class,xc);
// Get formula string
String formulaStr = (String) xPSxEO.getPropertyValue("Formula");
/* Try not creating any references */
Logger.getLogger("com.CompPad").log(Level.FINE," "+formulaStr);
/* Create CompPad expression */
/* dispose to prevent reference issues.*/
// xc.dispose();
return formulaStr;
}
/* set the value of the formula string */
public void setFormulaString(String s) throws java.lang.Exception {
Logger.getLogger("com.CompPad").log(Level.FINE,"setFormulaString: "+s);
XComponent xc = xEOS.getEmbeddedObject();
XPropertySet xPSxEO =
(XPropertySet) UnoRuntime
.queryInterface(XPropertySet.class,xc);
// For some reason, this is coming up null once in a while. If so, then get by name -
// THIS SHOULD BE IN AN OOODOCUMENT METHOD
if (xPSxEO==null){
xc = ((XEmbeddedObjectSupplier)((com.sun.star.uno.Any)((XTextEmbeddedObjectsSupplier)UnoRuntime.queryInterface(
XTextEmbeddedObjectsSupplier.class,xTextDocument))
.getEmbeddedObjects()
.getByName(name)).getObject())
.getEmbeddedObject();
Logger.getLogger("com.CompPad").log(Level.FINE,"xc="+xc+" name="+name);
xPSxEO = (XPropertySet) UnoRuntime
.queryInterface(XPropertySet.class,xc);
}
// Get formula string
xPSxEO.setPropertyValue("Formula",s);
/* dispose to prevent reference issues.*/
// xc.dispose();
}
@Override
public String getName() {
return name;
}
@Override
public String toString() {
String formulaString=null;
try{
formulaString=getFormulaString();
} catch (java.lang.Exception ex){
}
return "[OOOExpression: "+formulaString+"]";
}
/* this listener class is called by the Expression class
* in order to set the output value from the expression.
* Find a dependent element
* that can handle the result. The OOOExpression object itself
* will be the first dependent object in the list.
* If the dependent element does not exist, then create it.
*/
private class OOOExpressionListener implements Listener{
public void refresh() {
throw new UnsupportedOperationException("Not supported yet.");
}
@SuppressWarnings("static-access")
public void setValue(Object arg) throws Exception{
/* If message handler doesn't exist, then create it, and call
* listener */
/* Better way to do this would be to create hashtable with
* messageType:outputType pairs */
/* Check dependent elemenet and query for the type
* expected by the listener. If not found, then
create new element that matches type */
boolean handled=false;
if (dependentElement != null){
if (dependentElement.canHandle(arg)){
try{
dependentElement.handle(arg);
handled=true;
}
catch (java.lang.Exception ex){
arg = ex;
}
}
else{
/* If it cannot handle arg, then dispose of it */
try{
// dependentElement.dispose();
}
catch(Exception e){
Logger.getLogger("com.CompPad").log(Level.FINE,
"dependentElement: "+dependentElement);
Logger.getLogger("com.CompPad").log(Level.FINE,
"Error disposing element");
}
dependentElement=null;
}
}
if (dependentElement==null){
for (OOOElement e : oooDocument.elementTypes){
if ( e.canHandle(arg)){
dependentElement=
(OOOElement) e.getClass().getConstructor(OOODocument.class,
OOOExpression.class).newInstance(oooDocument,
thisx); /* get constructor! */
try{
dependentElement.handle(arg);
handled=true;
break;
}
catch (java.lang.Exception ex){
arg = ex;
}
}
else{
}
}
}
if (!handled & arg!=null){
/* Display error! */
dependentElement=new OOOError(oooDocument, thisx);
Logger.getLogger("com.CompPad").log(Level.FINE, "No element to handle data type: "+ arg);
if (java.lang.Exception.class.isInstance(arg)){
((java.lang.Exception)arg).printStackTrace();
dependentElement.handle(arg);
}
else{
dependentElement.handle(new java.lang.Error(
" No element to handle data type: "
+arg.getClass().getName()));
}
}
}
public Object getValue() {
throw new UnsupportedOperationException("Not supported yet.");
}
}
@Override
public boolean canHandle(Object e) throws Exception{
for (Class v : valueTypes){
if (v.isInstance(e)){
/* Note that this is satisfied if e is a subclass of v
* eg. Object.isInstance(Integer) would return true,
* but Integer.isInstance(Object would return false,
* so put classes in valueTypes list with subclasses
* (eg Integer) first */
return true;
}
}
return false;
}
public void handle(Object e) throws Exception{
Logger.getLogger("com.CompPad").log(Level.FINE,"HANDLING "+e);
/* set the result value in the expression object */
expression.setResult(e);
/* get updated expression string */
String s = expression.getFormulaStr();
/* set the OOO formula string to updated string */
this.setFormulaString(s);
}
}