/* 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.ComplexAmount;
import com.CompPad.model.Matrix;
import com.CompPad.model.Plot;
import com.sun.star.beans.XPropertySet;
import com.sun.star.chart.ChartAxisPosition;
import com.sun.star.chart.ChartSymbolType;
import com.sun.star.chart.XAxisXSupplier;
import com.sun.star.chart.XAxisYSupplier;
import com.sun.star.chart.XChartDataArray;
import com.sun.star.chart.XChartDocument;
import com.sun.star.chart.XDiagram;
import com.sun.star.container.XNamed;
import com.sun.star.document.XEmbeddedObjectSupplier;
import com.sun.star.drawing.XShape;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.text.TextContentAnchorType;
import com.sun.star.text.XTextContent;
import com.sun.star.text.XTextCursor;
import com.sun.star.text.XTextRange;
import com.sun.star.uno.Any;
import com.sun.star.uno.Type;
import com.sun.star.uno.UnoRuntime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.measure.unit.Unit;
import org.jscience.mathematics.number.Float64;
import sun.nio.cs.ext.ISCII91;
/**
* Wrapper for openoffice chart for plotting comppad results
* @author Toby D. Rule
*/
/* Refer to the following page on how to insert a chart */
/* http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/Text/Embedded_Objects */
public class OOOPlot extends OOOElement{
private Class[] valueTypes={Plot.class};
private OOODocument oooDocument;
private OOOExpression oooExpression;
private XEmbeddedObjectSupplier xEOS;
private String name;
private Plot plot;
private XChartDocument xChartDoc;
public OOOPlot(){
}
public OOOPlot(OOODocument docArg, OOOExpression expArg){
oooExpression = expArg;
oooDocument = docArg;
Logger.getLogger("com.CompPad").log(Level.FINE,"Initializing OOOPlot, oooExpression = "
+oooExpression+" oooDocument = "+oooDocument);
xTextDocument=docArg.getTextDocument();
}
public OOOPlot(OOODocument docArg,XEmbeddedObjectSupplier xEOS){
this.xEOS=xEOS;
oooDocument = docArg;
XNamed xNxEOS = (XNamed) UnoRuntime.queryInterface(XNamed.class, xEOS);
// Get name of object and use as name for expression
name = xNxEOS.getName();
xTextDocument=docArg.getTextDocument();
}
public boolean canHandle(Object e) throws Exception{
for (Class v : valueTypes){
if (v.isInstance(e)){
Logger.getLogger("com.CompPad").log(Level.FINE,"Can Handle Plot!");
return true;
}
}
return false;
}
@Override
public XTextRange getTextRange(){
XTextContent xTCxEOS = (XTextContent)
UnoRuntime.queryInterface(XTextContent.class, xEOS);
XTextRange xTextRange = xTCxEOS.getAnchor();
return xTextRange;
}
@Override
public void setDependsOn(OOOExpression arg){
oooExpression=arg;
}
public String getName() {
return name;
}
@Override
public void handle(Object e) throws Exception{
/* Note that this is using the old API, not chart2.
* I should probably update this to use the newer API */
/* Information on the following web page: */
/* http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/Charts/Charts */
XMultiServiceFactory chartMSF;
XDiagram aDiagram;
/* Either update existing plot, or create a new one */
/* Should check this! */
plot = (Plot)e;
Object obj = plot.getObject();
if (Matrix.class.isInstance(obj)){
/* Plot real components, assume that they are dimensionless */
/* Inner sequence represents rows */
/* New method: */
/* 2 n columns consisting of n x-y pairs */
/* m rows, but some rows may be null */
/* plotting expeccts an x column and n y-columns, so we'll
* create a list of rows with n+1 elements, then convert to array */
/* Inner sequence represents rows */
// x and y units
javax.measure.unit.Unit xunits=null;
javax.measure.unit.Unit yunits=null;
// strings that will be used to set x and y labels
String xUnitString;
String yUnitString;
com.CompPad.model.EvalUnit xEvalUnit = null;
com.CompPad.model.EvalUnit yEvalUnit = null;
Matrix mmatrix = (Matrix) plot.getObject();
int m = (int) Math.round(mmatrix.getNumberOfRows().getReal(Unit.ONE));
int n = (int) Math.round(mmatrix.getNumberOfColumns().getReal(Unit.ONE)) / 2;
String[] colDescriptions = new String[n+1];
colDescriptions[0] = "x";
List<Double[]> x = new ArrayList(); /* List of x/y pairs */
Double newRow[];
int mm = 0; /* Total number of rows after loop */
// iterate columns
for (int j=0; j<n; ++j){
/* Create column label sequence */
colDescriptions[j+1] = "series "+(j+1);
// iterate rows
for (int i=0; i<m;++i){
if (mmatrix.get(i,2*j)==null){
/* Set "no data" value */
}
else {
newRow = new Double[n+1];
// Arrays.fill(newRow,Double.MIN_NORMAL);
Arrays.fill(newRow,Math.pow(2.0, -1022.0));
// x value
ComplexAmount a1 = (ComplexAmount)mmatrix.get(i,2*j);
// y value
ComplexAmount a2 = (ComplexAmount)mmatrix.get(i,2*j+1);
// Check whether units have been defined
if (xunits==null){
// if not then define x and y units
xunits = a1.getUnit();
yunits = a2.getUnit();
// get units string for system units. These will later
// be assigned to x and y axes.
// Must get evaluator to get a units string.
com.CompPad.model.Evaluator evaluator
= oooDocument.getCompPadDocument().getEvaluator();
xEvalUnit = evaluator.getUnits(a1);
yEvalUnit = evaluator.getUnits(a2);
// these units will be based on the unit system
// defined in the evaluator.
}
else{
// If units have been defined, then check that the
// units of the current row are compatible.
if (!xunits.isCompatible(a1.getUnit()) || !yunits.isCompatible(a2.getUnit())){
throw new Exception("Inconsistant dimension in arguments to plotxy.");
}
else{
// otherwise, we're good.
// divide out by our x and y units
newRow[0] = a1.divide(xEvalUnit.getValue()).getReal(Unit.ONE);
newRow[j+1] = a2.divide(yEvalUnit.getValue()).getReal(Unit.ONE);
x.add(newRow);
mm++;
}
}
}
}
}
// convert list to array
double[][] ar = new double[mm][n+1];
for (int i = 0; i < mm; ++i){
for (int j=0; j<n+1; ++j){
ar[i][j] = x.get(i)[j];
}
}
boolean newChart = true;
// check if chart exists
if (xEOS!=null){
xChartDoc = (XChartDocument)UnoRuntime.queryInterface(
XChartDocument.class,xEOS.getEmbeddedObject());
newChart = false;
/* For some reason, it is sometimes necessary, especially with a
plot located at the end of a long document, to hit this twice
to get a non-null xChartDoc!!! */
if (xChartDoc==null){
xChartDoc = (XChartDocument)UnoRuntime.queryInterface(
XChartDocument.class,xEOS.getEmbeddedObject());
newChart = false;
}
}
else{
XMultiServiceFactory xMSF =
(XMultiServiceFactory) UnoRuntime.queryInterface(
XMultiServiceFactory.class,
oooDocument.getTextDocument());
XTextContent xTC =
(XTextContent)(UnoRuntime.queryInterface(
XTextContent.class,xMSF.createInstance(
"com.sun.star.text.TextEmbeddedObject")));
/* Position chart inline. */
XPropertySet xFrameProps =
(XPropertySet)UnoRuntime.queryInterface(
XPropertySet.class, xTC );
xFrameProps.setPropertyValue( "AnchorType",
TextContentAnchorType.AS_CHARACTER );
xFrameProps.setPropertyValue("CLSID", OOODocument.CLSID_CHART);
XTextCursor xTextCursor=oooExpression.getTextCursor();
xTextCursor.goRight((short) 1, false);
xTextCursor.getText().insertTextContent(
xTextCursor, xTC, false);
XComponent xModel=
((XEmbeddedObjectSupplier) UnoRuntime.queryInterface(
XEmbeddedObjectSupplier.class,xTC)).getEmbeddedObject();
xChartDoc= (XChartDocument)UnoRuntime.queryInterface(
XChartDocument.class, xModel);
chartMSF=
(XMultiServiceFactory)UnoRuntime.queryInterface(
XMultiServiceFactory.class, xChartDoc);
aDiagram = (XDiagram) UnoRuntime.queryInterface(
XDiagram.class,
chartMSF.createInstance("com.sun.star.chart.XYDiagram"));
// if x or y values are not dimensionless, then set titles
XShape yTitleShape = ((XAxisYSupplier) UnoRuntime.queryInterface(XAxisYSupplier.class, aDiagram))
.getYAxisTitle();
XShape xTitleShape = ((XAxisXSupplier) UnoRuntime.queryInterface(XAxisXSupplier.class, aDiagram))
.getXAxisTitle();
if (xEvalUnit.getName()!=null){
((XPropertySet) UnoRuntime.queryInterface(XPropertySet.class, aDiagram))
.setPropertyValue("HasXAxisTitle", true);
((XPropertySet) UnoRuntime.queryInterface(XPropertySet.class, xTitleShape))
.setPropertyValue("String", "["+xEvalUnit.getName()+"]");
}
if (yEvalUnit.getName()!=null){
((XPropertySet) UnoRuntime.queryInterface(XPropertySet.class, aDiagram))
.setPropertyValue("HasYAxisTitle", true);
((XPropertySet) UnoRuntime.queryInterface(XPropertySet.class, yTitleShape))
.setPropertyValue("String", "["+yEvalUnit.getName()+"]");
}
/* Default to no points */
XPropertySet chartXPS = (XPropertySet)UnoRuntime.queryInterface(
XPropertySet.class,aDiagram);
/* Fix formatting so it looks good (X and Y Grid,
* no markers by default) */
chartXPS.setPropertyValue("SymbolType", ChartSymbolType.NONE);
chartXPS.setPropertyValue("HasYAxisGrid",false);
xChartDoc.setDiagram(aDiagram);
// set xaxis to cross y-axis at minimum value
((XAxisXSupplier) UnoRuntime.queryInterface(XAxisXSupplier.class, aDiagram))
.getXAxis()
.setPropertyValue("CrossoverPosition",ChartAxisPosition.START );
// set xaxis to cross y-axis at minimum value
((XAxisYSupplier) UnoRuntime.queryInterface(XAxisYSupplier.class, aDiagram))
.getYAxis()
.setPropertyValue("CrossoverPosition",ChartAxisPosition.START );
long legendWidth = xChartDoc.getLegend().getSize().Width
+ yTitleShape.getSize().Width
+ yTitleShape.getPosition().X + 6000;
// Must convert Long to Any to do setProperty
xFrameProps.setPropertyValue("Width", new Any(Type.LONG,legendWidth));
Integer xTitleHeight;
if (xTitleShape.getPosition().Y==0){
xTitleHeight = 0;
}
else{
// I think Y position will be distance to the top corner,
// so I do not also need the height of the XTitleShape.
xTitleHeight = (Integer)xFrameProps.getPropertyValue("Height")
- xTitleShape.getPosition().Y;
}
xFrameProps.setPropertyValue("Height",
xTitleHeight+ 6000);
}
/* Set chart data */
XChartDataArray xDataArray=
(XChartDataArray)UnoRuntime.queryInterface(
XChartDataArray.class, xChartDoc.getData());
xDataArray.setData(ar);
if (newChart){
/* Set row descriptions */
xDataArray.setColumnDescriptions(colDescriptions);
}
}
}
@Override
public void dispose() throws Exception{
if (xChartDoc!=null){
xChartDoc.dispose();
}
}
}