package org.spaceroots.rkcheck;
import java.math.BigInteger;
import java.math.BigDecimal;
import java.util.ArrayList;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* This class reads or write an XML file containing a Runge-Kutta
* method description.
* @version $Id: RungeKuttaFile.java,v 1.2 2004/05/23 18:36:27 luc Exp $
* @author L. Maisonobe
*/
public class RungeKuttaFile
extends XMLFile {
/** Simple constructor.
* Build a file reader/writer for a Runge-Kutta method instance.
* @param method Runge-Kutta method instance to initialize by file
* reading or to store by file writing
* @exception RKCheckException if no parser can be built
*/
public RungeKuttaFile(RungeKuttaMethod method)
throws RKCheckException {
this.method = method;
}
/** Analyze the DOM tree.
* This method is called by the {@link #read read} method after
* having build the DOM tree. Its purpose is to analyze the tree and
* store the contained information in application-specific ways.
* @param document DOM tree to analyze
* @param uri URI from which the project document was read (useful
* mainly for error messages)
* @exception RKCheckException if some data cannot be found in the tree
* or if there is a dimension mismatch
* @exception NumberFormatException if some number cannot be parsed
*/
public void analyze(Document document, String uri)
throws RKCheckException, NumberFormatException {
// root element
Element rkElt = getElement(document, EltRungeKutta, false);
method.setName(getAttribute(rkElt, AttRungeKuttaName, false));
// time steps
QuadraticSurd[] timeSteps = analyzeList(getElement(rkElt, EltTime, false));
int stages = timeSteps.length;
method.setTimeSteps(timeSteps);
// internal weights
QuadraticSurd[][] internalWeights = new QuadraticSurd[stages][];
NodeList list = getElement(rkElt, EltInternal, false).getChildNodes();
int s = 0;
for (int i = 0; i < list.getLength(); ++i) {
Node row = list.item(i);
if ((row.getNodeType() == Node.ELEMENT_NODE)
&& (row.getNodeName().equals(EltRow))) {
internalWeights[s] = analyzeList(row);
if (internalWeights[s].length != s) {
throw new RKCheckException("row {0} has {1} elements, but should have {2} elements",
new String[] {
Integer.toString(s + 1),
Integer.toString(internalWeights[s].length),
Integer.toString(s)
});
}
++s;
}
}
if (s != stages) {
throw new RKCheckException("internal weights array has {0} rows,"
+ " but time steps array has {1} elements",
new String[] {
Integer.toString(s),
Integer.toString(stages)
});
}
method.setInternalWeights(internalWeights);
// estimation weights
QuadraticSurd[] estimationWeights = analyzeList(getElement(rkElt, EltEstimation, false));
if (estimationWeights.length != stages) {
throw new RKCheckException("estimation weights array has {0} rows,"
+ " but time steps array has {1} elements",
new String[] {
Integer.toString(estimationWeights.length),
Integer.toString(stages)
});
}
method.setEstimationWeights(estimationWeights);
// error weights
Element errorElt = getElement(rkElt, EltError, true);
if (errorElt != null) {
QuadraticSurd[] errorWeights = analyzeList(errorElt);
if (errorWeights.length != stages) {
throw new RKCheckException("error weights array has {0} rows,"
+ " but time steps array has {1} elements",
new String[] {
Integer.toString(errorWeights.length),
Integer.toString(stages)
});
}
method.setErrorWeights(errorWeights);
}
}
/** Analyze a list of numbers.
* @param element element containing the list
* @return an array containing the numbers
* @exception RKCheckException if a node contains unexpected elements
*/
private QuadraticSurd[] analyzeList(Node element)
throws RKCheckException {
ArrayList numbers = new ArrayList();
NodeList list = element.getChildNodes();
for (int i = 0; i < list.getLength(); ++i) {
Node number = list.item(i);
if (number.getNodeType() == Node.ELEMENT_NODE) {
if (number.getNodeName().equals(EltZero)) {
numbers.add(new QuadraticSurd(0));
} else if (number.getNodeName().equals(EltOne)) {
numbers.add(new QuadraticSurd(1));
} else if (number.getNodeName().equals(EltInteger)) {
numbers.add(new QuadraticSurd(new BigInteger(getContent(number).trim())));
} else if (number.getNodeName().equals(EltRational)) {
BigInteger p = new BigInteger(getContent(getElement(number, EltP, false)).trim());
BigInteger q = new BigInteger(getContent(getElement(number, EltQ, false)).trim());
numbers.add(new QuadraticSurd(p, q));
} else if (number.getNodeName().equals(EltQSurd)) {
BigInteger p1 = new BigInteger(getContent(getElement(number, EltP1, false)).trim());
BigInteger p2 = new BigInteger(getContent(getElement(number, EltP2, false)).trim());
BigInteger d = new BigInteger(getContent(getElement(number, EltD, false)).trim());
BigInteger q = new BigInteger(getContent(getElement(number, EltQ, false)).trim());
numbers.add(new QuadraticSurd(p1, p2, d, q));
} else if (number.getNodeName().equals(EltReal)) {
BigDecimal r = new BigDecimal(getContent(number).trim());
BigDecimal tol = new BigDecimal(BigInteger.ONE, r.scale());
numbers.add(new QuadraticSurd(r, tol));
}
}
}
return (QuadraticSurd[]) numbers.toArray(new QuadraticSurd[numbers.size()]);
}
/** Build the DOM tree.
* This method is called by the {@link #write write} method
* before writing the DOM tree. Its purpose is to build the DOM tree
* in application-specific ways.
* @param document empty DOM tree to build
* @param uri URI where the project document will be written (useful
* mainly for error messages)
* @exception RKCheckException if some problem occurs while building
* the tree
*/
public void build(Document document, String uri)
throws RKCheckException {
// root element
Element rkElt = (Element) document.createElement(EltRungeKutta);
rkElt.setAttribute(AttRungeKuttaName, method.getName());
document.appendChild(rkElt);
// time steps
Element timeElt = buildList(document, EltTime, method.getTimeSteps());
rkElt.appendChild(timeElt);
// internal weights
QuadraticSurd[][] internalWeights = method.getInternalWeights();
Element internalElt = (Element) document.createElement(EltInternal);
for (int i = 0; i < internalWeights.length; ++i) {
internalElt.appendChild(buildList(document, EltRow, internalWeights[i]));
}
rkElt.appendChild(internalElt);
// estimation weights
Element estimationElt = buildList(document, EltEstimation, method.getEstimationWeights());
rkElt.appendChild(estimationElt);
// error weights
if (method.getErrorWeights() != null) {
Element errorWeights = buildList(document, EltError, method.getErrorWeights());
rkElt.appendChild(errorWeights);
}
}
/** Build an element containing a list of numbers.
* @param document document containing the element to build
* @param name name of the element
* @return numbers array containing the numbers
*/
private Element buildList(Document document, String name, QuadraticSurd[] numbers) {
Element element = (Element) document.createElement(name);
for (int i = 0; i < numbers.length; ++i) {
QuadraticSurd number = numbers[i];
if (number.isZero()) {
element.appendChild(document.createElement(EltZero));
} else if (number.isOne()) {
element.appendChild(document.createElement(EltOne));
} else if (number.isInteger()) {
BigInteger n = number.getRationalNumerator();
Element integerElt = (Element) document.createElement(EltInteger);
integerElt.appendChild(document.createTextNode(n.toString()));
element.appendChild(integerElt);
} else if (number.isRational()) {
Element rationalElt = (Element) document.createElement(EltRational);
Element pElt = (Element) document.createElement(EltP);
pElt.appendChild(document.createTextNode(number.getRationalNumerator().toString()));
Element qElt = (Element) document.createElement(EltQ);
qElt.appendChild(document.createTextNode(number.getDenominator().toString()));
rationalElt.appendChild(pElt);
rationalElt.appendChild(qElt);
element.appendChild(rationalElt);
} else {
Element qSurdElt = (Element) document.createElement(EltQSurd);
Element p1Elt = (Element) document.createElement(EltP1);
p1Elt.appendChild(document.createTextNode(number.getRationalNumerator().toString()));
Element p2Elt = (Element) document.createElement(EltP2);
p2Elt.appendChild(document.createTextNode(number.getRootCoefficient().toString()));
Element dElt = (Element) document.createElement(EltD);
dElt.appendChild(document.createTextNode(number.getRootElement().toString()));
Element qElt = (Element) document.createElement(EltQ);
qElt.appendChild(document.createTextNode(number.getDenominator().toString()));
qSurdElt.appendChild(p1Elt);
qSurdElt.appendChild(p2Elt);
qSurdElt.appendChild(dElt);
qSurdElt.appendChild(qElt);
element.appendChild(qSurdElt);
}
}
return element;
}
/** Get the public ID of the DTD.
* @return public ID of the DTD
*/
protected String getPublicId() {
return "-//spaceroots.org//DTD Runge-Kutta V1.2//EN";
}
/** Get the system ID of the DTD.
* @return system ID of the DTD
*/
protected String getSystemId() {
return "http://www.spaceroots.org/Runge-Kutta.dtd";
}
/** Elements names. */
private static String EltRungeKutta = "Runge-Kutta";
private static String EltTime = "time-steps";
private static String EltInternal = "internal-weights";
private static String EltEstimation = "estimation-weights";
private static String EltError = "error-weights";
private static String EltRow = "row";
private static String EltZero = "zero";
private static String EltOne = "one";
private static String EltInteger = "integer";
private static String EltRational = "rational";
private static String EltQSurd = "quadratic-surd";
private static String EltReal = "real";
private static String EltP = "p";
private static String EltQ = "q";
private static String EltP1 = "p1";
private static String EltP2 = "p2";
private static String EltD = "d";
/** Attributes names. */
private static String AttRungeKuttaName = "name";
/** Runge-Kutta method to read/write */
RungeKuttaMethod method;
}
Ads help maintain this website.