Subversion Repositories general

Compare Revisions

No changes between revisions

Ignore whitespace Rev 1004 → Rev 1005

/zpath/trunk/src/ak/zpath/PathCreator.java
0,0 → 1,367
package ak.zpath;
 
import java.util.List;
import java.util.ArrayList;
import java.text.ParseException;
 
public class PathCreator
{
private PathElement element;
private List elements;
private PathParser pathParser = new PathParser();
 
public PathCreator()
{
}
 
public List createPath(String path)
throws ParseException
{
List tokens = pathParser.parse(path);
Token firstToken;
int pos = -1;
 
if(tokens.size() == 0)
throw new PathCreateException("Empty path", null);
 
firstToken = (Token)tokens.get(0);
elements = new ArrayList();
 
if(firstToken.getType() == Token.TOKEN_SLASH) {
// the path starts with slash
PathElement slashElement = new PathElement();
 
slashElement.setName("/");
elements.add(slashElement);
pos = 0;
}
 
element = null;
stateElementStart(tokens, pos);
if(element != null) elements.add(element); // add the last element
 
return elements;
}
 
private int stateElementStart(List tokens, int pos)
throws PathCreateException
{
Token token = (Token)tokens.get(++pos);
 
if(element != null) elements.add(element);
element = new PathElement();
 
switch(token.getType()) {
case Token.TOKEN_NAME:
element.setName(token.getValue());
pos = stateElementName(tokens, pos);
break;
 
default:
throw new PathCreateException("Element name expected", token);
}
 
return pos;
}
 
private int stateElementName(List tokens, int pos)
throws PathCreateException
{
if(++pos >= tokens.size()) return pos;
 
Token token = (Token)tokens.get(pos);
 
switch(token.getType()) {
case Token.TOKEN_SLASH:
pos = stateElementStart(tokens, pos);
break;
 
case Token.TOKEN_OPEN_BRACKET:
pos = stateElementCondition(tokens, pos);
break;
 
case Token.TOKEN_SHARP:
pos = stateOperandText(tokens, pos);
break;
 
case Token.TOKEN_AT:
pos = stateOperandAttribute(tokens, pos);
break;
 
default:
throw new PathCreateException(
"Element condition or operand expected", token);
}
 
return pos;
}
 
private int stateOperandText(List tokens, int pos)
throws PathCreateException
{
Token token = (Token)tokens.get(++pos);
 
element.setFirstOperand(PathElement.OPERAND_TEXT, null);
 
switch(token.getType()) {
case Token.TOKEN_ASSIGN:
element.setOperation(PathElement.OPERATION_ASSIGN);
pos = stateSecondOperand(tokens, pos);
break;
 
case Token.TOKEN_APPEND:
element.setOperation(PathElement.OPERATION_APPEND);
pos = stateSecondOperand(tokens, pos);
break;
 
default:
throw new PathCreateException(
"Assign or append operation expected", token);
}
 
return pos;
}
 
private int stateOperandAttribute(List tokens, int pos)
throws PathCreateException
{
Token token = (Token)tokens.get(++pos);
 
switch(token.getType()) {
case Token.TOKEN_NAME: {
element.setFirstOperand(PathElement.OPERAND_ATTRIBUTE, token.getValue());
token = (Token)tokens.get(++pos);
switch(token.getType()) {
case Token.TOKEN_ASSIGN:
element.setOperation(PathElement.OPERATION_ASSIGN);
pos = stateSecondOperand(tokens, pos);
break;
 
case Token.TOKEN_APPEND:
element.setOperation(PathElement.OPERATION_APPEND);
pos = stateSecondOperand(tokens, pos);
break;
 
default:
throw new PathCreateException(
"Assign or append operation expected", token);
}
break;
}
 
default:
throw new PathCreateException("Attribute name expected", token);
}
 
return pos;
}
 
private int stateSecondOperand(List tokens, int pos)
throws PathCreateException
{
Token token = (Token)tokens.get(++pos);
 
switch(token.getType()) {
case Token.TOKEN_VARIABLE:
element.setSecondOperand(PathElement.OPERAND_VARIABLE, token.getValue());
if(pos < tokens.size() - 1)
throw new PathCreateException("Path end expected", token);
break;
 
case Token.TOKEN_STRING:
element.setSecondOperand(PathElement.OPERAND_STRING, token.getValue());
if(pos < tokens.size() - 1)
throw new PathCreateException("Path end expected", token);
break;
 
default:
throw new PathCreateException("Second operand expected", token);
}
 
element.validateOperands(); // must be end of the path
 
return pos;
}
 
private int stateElementCondition(List tokens, int pos)
throws PathCreateException
{
Token token = (Token)tokens.get(++pos);
 
switch(token.getType()) {
case Token.TOKEN_NEW:
pos = stateNew(tokens, pos);
break;
 
case Token.TOKEN_SHARP:
case Token.TOKEN_AT:
pos = stateLogicalCondition(tokens, pos);
break;
 
default:
throw new PathCreateException("Element condition expected", token);
}
 
return pos;
}
 
private int stateLogicalCondition(List tokens, int pos)
throws PathCreateException
{
Token token = (Token)tokens.get(pos); // do not go to the next token
 
switch(token.getType()) {
case Token.TOKEN_SHARP:
pos = stateConditionText(tokens, pos);
break;
 
case Token.TOKEN_AT:
pos = stateConditionAttribute(tokens, pos);
break;
 
default:
throw new PathCreateException("Element condition expected", token);
}
 
return pos;
}
 
private int stateConditionText(List tokens, int pos)
throws PathCreateException
{
Token token = (Token)tokens.get(++pos);
 
element.addCondition();
element.setFirstCondition(PathElement.OPERAND_TEXT, null);
 
switch(token.getType()) {
case Token.TOKEN_EQUAL:
element.setConditionOperation(PathElement.OPERATION_EQUAL);
pos = stateConditionSecond(tokens, pos);
break;
 
default:
throw new PathCreateException("Compare operator expected", token);
}
 
return pos;
}
 
private int stateConditionAttribute(List tokens, int pos)
throws PathCreateException
{
Token token = (Token)tokens.get(++pos);
 
element.addCondition();
 
switch(token.getType()) {
case Token.TOKEN_NAME: {
element.setFirstCondition(
PathElement.OPERAND_ATTRIBUTE, token.getValue());
token = (Token)tokens.get(++pos);
switch(token.getType()) {
case Token.TOKEN_EQUAL:
element.setConditionOperation(PathElement.OPERATION_EQUAL);
pos = stateConditionSecond(tokens, pos);
break;
 
default:
throw new PathCreateException("Compare operation expected", token);
}
break;
}
 
default:
throw new PathCreateException("Attibute name expected", token);
}
 
return pos;
}
 
private int stateConditionSecond(List tokens, int pos)
throws PathCreateException
{
Token token = (Token)tokens.get(++pos);
switch(token.getType()) {
case Token.TOKEN_VARIABLE:
element.setSecondCondition(
PathElement.OPERAND_VARIABLE, token.getValue());
break;
 
case Token.TOKEN_STRING:
element.setSecondCondition(
PathElement.OPERAND_STRING, token.getValue());
break;
 
default:
throw new PathCreateException(
"Second condition operand expected", token);
}
element.validateCondition(); // must be end of condition
 
token = (Token)tokens.get(++pos);
switch(token.getType()) {
case Token.TOKEN_CLOSE_BRACKET:
pos = stateConditionEnd(tokens, pos);
break;
 
case Token.TOKEN_AND:
pos++; // go to next token for stateLogicalCondition
pos = stateLogicalCondition(tokens, pos);
break;
 
default:
throw new PathCreateException(
"End of conditions or next condition expected", token);
}
 
return pos;
}
 
private int stateNew(List tokens, int pos)
throws PathCreateException
{
Token token = (Token)tokens.get(++pos);
 
element.setNew();
 
switch(token.getType()) {
case Token.TOKEN_CLOSE_BRACKET:
pos = stateConditionEnd(tokens, pos);
break;
 
default:
throw new PathCreateException("End of conditions expected", token);
}
 
return pos;
}
 
private int stateConditionEnd(List tokens, int pos)
throws PathCreateException
{
if(++pos >= tokens.size()) return pos;
 
Token token = (Token)tokens.get(pos);
 
switch(token.getType()) {
case Token.TOKEN_SLASH:
stateElementStart(tokens, pos); // start new path element
break;
 
case Token.TOKEN_SHARP:
pos = stateOperandText(tokens, pos);
break;
 
case Token.TOKEN_AT:
pos = stateOperandAttribute(tokens, pos);
break;
 
default:
throw new PathCreateException(
"End of element description expected", token);
}
 
return pos;
}
}
/zpath/trunk/src/ak/zpath/DocumentTree.java
0,0 → 1,550
package ak.zpath;
 
import org.w3c.dom.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import java.io.*;
import java.util.*;
import java.text.ParseException;
 
public class DocumentTree
{
/**
* if true then null values of parameters will be replaced with empty strings
*/
private boolean nullDefaultEmptyString = true;
 
/**
* if true then execution will be terminated if desired node for assigment
* has already a value
*/
private boolean failOnValueSet = true;
 
private PathCreator pathCreator = new PathCreator();
private Document document;
private Element lastElement = null;
 
public DocumentTree(Document document)
{
this.document = document;
}
 
public void updateDocument(String path, Object[] values)
throws ParseException
{
updateElement(path, values);
}
 
public Element updateElement(String path, Object[] values)
throws ParseException
{
return updateElement(null, path, values);
}
 
public Element updateElement(Element start, String path, Object[] values)
throws ParseException
{
return updateElement(start, path, createParamsMap(values));
}
 
public Element updateElement(String path, Map params)
throws ParseException
{
return updateElement(null, path, params);
}
 
public Element updateElement(Element start, String path, Map params)
throws ParseException
{
List elements = pathCreator.createPath(path);
Element target = searchElementInternal(start, elements, params, true);
 
updateTarget(
(PathElement)elements.get(elements.size() - 1), params, target);
 
return target;
}
 
public Element searchElement(String path, Object[] values)
throws ParseException
{
return searchElement(null, path, values);
}
 
public Element searchElement(Element start, String path, Object[] values)
throws ParseException
{
return searchElement(start, path, createParamsMap(values));
}
 
public Element searchElement(String path, Map params)
throws ParseException
{
return searchElement(null, path, params);
}
 
public Element searchElement(Element start, String path, Map params)
throws ParseException
{
return searchElementInternal(start,
pathCreator.createPath(path), params, false);
}
 
protected Element searchElementInternal(Element start, List path, Map params,
boolean update)
throws ParseException
{
Element root = document.getDocumentElement();
PathElement element;
Element target = null;
int startPos = 0;
 
if(path.size() == 0)
throw new PathCreateException("No elements found in path");
 
element = (PathElement)path.get(0);
 
if("/".equals(element.getName())) { // absolute path
element = (PathElement)path.get(1);
 
if(path.size() > 0 && "**".equals(element.getName())) {
// special case: '/**'
startPos = 2;
}
else {
if(root == null) {
if(!update) return null;
 
root = document.createElement(element.getName());
document.appendChild(root);
lastElement = root;
}
else if(!root.getNodeName().equals(element.getName())) {
if(!update)
return null;
else
throw new PathCreateException(
"Document has already root element with name '"
+ root.getNodeName() + "'");
}
 
target = root;
startPos = 2;
}
}
 
if("**".equals(element.getName())) { // ref to last created element
if(lastElement == null)
throw new PathCreateException(
"Cannot go to '**' because no elements was created yet");
 
target = lastElement;
startPos++;
}
else if(startPos == 0) { // it was no slash element at the begin
if(start == null)
throw new PathCreateException(
"No start element specified for relative path");
 
target = start;
startPos = 0;
}
 
target = doStep(path, startPos, params, target, update);
 
return target;
}
 
protected void updateTarget(PathElement element, Map params, Element target)
throws ParseException
{
Node node = null;
String value;
 
// get first value
switch(element.getFirstOperandType()) {
case PathElement.OPERAND_NONE:
return; // nothing to do
 
case PathElement.OPERAND_TEXT:
NodeList list = target.getChildNodes();
boolean done = false;
for(int i = 0; i < list.getLength(); i++) {
Node subnode = list.item(i);
if(subnode.getNodeType() == Node.TEXT_NODE) {
node = subnode;
done = true;
break;
}
}
if(!done) {
node = document.createTextNode(null);
target.appendChild(node);
}
break;
 
case PathElement.OPERAND_ATTRIBUTE:
node = target.getAttributeNode(element.getFirstOperandValue());
if(node == null) {
node = document.createAttribute(element.getFirstOperandValue());
target.setAttributeNode((Attr)node);
}
break;
 
default:
throw new RuntimeException("Unknown first operand type: "
+ element.getFirstOperandType());
}
 
// get second value
switch(element.getSecondOperandType()) {
case PathElement.OPERAND_VARIABLE:
value = (String)params.get(element.getSecondOperandValue());
break;
 
case PathElement.OPERAND_STRING:
value = element.getSecondOperandValue();
break;
 
default:
throw new RuntimeException("Unknown second operand type: "
+ element.getSecondOperandType());
}
 
// set value
String oldValue = node.getNodeValue();
 
switch(element.getOperation()) {
case PathElement.OPERATION_ASSIGN:
// chech if value is already set
if(failOnValueSet && oldValue != null && !oldValue.equals(""))
throw new RuntimeException("Node " + node.getNodeName()
+ " has already value '" + oldValue + "'");
node.setNodeValue(value);
break;
 
case PathElement.OPERATION_APPEND:
if(oldValue == null) oldValue = "";
node.setNodeValue(oldValue + value);
break;
 
default:
throw new RuntimeException("Unknown operation: "
+ element.getOperation());
}
}
 
protected Map createParamsMap(Object[] values)
throws ParseException
{
Map map = new HashMap();
 
if(values != null) {
if(values.length % 2 != 0)
throw new PathCreateException(
"Number of values must match the number of names");
 
for(int i = 0; i < values.length; i += 2) {
if(!(values[i] instanceof String))
throw new PathCreateException("Name of value must be string");
 
if(values[i+1] == null)
;
else if(!(values[i+1] instanceof String))
throw new PathCreateException(
"Value must be string in current version");
 
// save the value
Object value = values[i+1];
 
if(nullDefaultEmptyString) {
if(value == null) value = "";
}
else
throw new PathCreateException(
"Cannot save null value of '" + values[i] + "' to XML");
 
map.put(values[i], value);
}
}
 
return map;
}
 
protected Element doStep(List path, int pos, Map params, Element node,
boolean update)
throws ParseException
{
if(pos >= path.size()) return node;
 
NodeList list = node.getChildNodes();
int count = list.getLength();
PathElement element = (PathElement)path.get(pos);
 
// special element name - parent element
if("..".equals(element.getName())) {
if(node.getParentNode() == null)
throw new PathCreateException(
"Node " + node.getNodeName() + " has no parent");
 
if(!(node.getParentNode() instanceof Element))
throw new PathCreateException(
"Parent of node " + node.getNodeName() + " is not an element");
 
return doStep(path, ++pos, params, (Element)node.getParentNode(), update);
}
 
// ordinal element without the 'new' keyword
if(!element.getNew()) {
for(int i = count - 1; i >= 0; i--) { // go backward
Node subnode = list.item(i);
 
if(subnode.getNodeType() != Node.ELEMENT_NODE)
continue; // go through elements only
 
Element e = (Element)subnode;
 
if(testElement(element, params, e)) {
return doStep(path, ++pos, params, e, update);
}
}
}
 
// nothing found
if(update) { // create a new one
if(element.getName() == null)
throw new PathCreateException("Cannot create element with name '*'");
 
Element newElement = document.createElement(element.getName());
setupElement(element, params, newElement);
node.appendChild(newElement);
lastElement = newElement;
 
return doStep(path, ++pos, params, newElement, update);
}
else { // not allowed to change document
return null;
}
}
 
protected void setupElement(PathElement element, Map params, Element node)
{
for(Iterator i = element.getConditions().iterator(); i.hasNext(); ) {
PathCondition condition = (PathCondition)i.next();
Node subnode = null;
String value;
 
// get first value
switch(condition.getFirstOperandType()) {
case PathElement.OPERAND_TEXT:
subnode = document.createTextNode("");
node.appendChild(subnode);
break;
 
case PathElement.OPERAND_ATTRIBUTE:
subnode = document.createAttribute(condition.getFirstOperandValue());
node.setAttributeNode((Attr)subnode);
break;
 
default:
throw new RuntimeException("Unknown first operand type: "
+ condition.getFirstOperandType());
}
 
// get second value
switch(condition.getSecondOperandType()) {
case PathElement.OPERAND_VARIABLE:
value = (String)params.get(condition.getSecondOperandValue());
break;
 
case PathElement.OPERAND_STRING:
value = condition.getSecondOperandValue();
break;
 
default:
throw new RuntimeException("Unknown second operand type: "
+ condition.getSecondOperandType());
}
 
// set
switch(condition.getOperation()) {
case PathElement.OPERATION_EQUAL:
subnode.setNodeValue(value);
break;
 
default:
throw new RuntimeException("Unknown operation: "
+ condition.getOperation());
}
}
}
 
protected boolean testElement(PathElement element, Map params, Element node)
{
// element name is specified and does not match
if(!element.getName().equals("*")
&& !node.getNodeName().equals(element.getName()))
{
return false;
}
 
for(Iterator i = element.getConditions().iterator(); i.hasNext(); ) {
PathCondition condition = (PathCondition)i.next();
String firstValue = null;
String secondValue;
 
// get first value
switch(condition.getFirstOperandType()) {
case PathElement.OPERAND_TEXT:
NodeList list = node.getChildNodes();
for(int j = 0; j < list.getLength(); j++) {
Node subnode = list.item(j);
if(subnode.getNodeType() == Node.TEXT_NODE) {
firstValue = subnode.getNodeValue();
break;
}
}
break;
 
case PathElement.OPERAND_ATTRIBUTE:
firstValue = node.getAttribute(condition.getFirstOperandValue());
break;
 
default:
throw new RuntimeException("Unknown first operand type: "
+ condition.getFirstOperandType());
}
 
// get second value
switch(condition.getSecondOperandType()) {
case PathElement.OPERAND_VARIABLE:
secondValue = (String)params.get(condition.getSecondOperandValue());
break;
 
case PathElement.OPERAND_STRING:
secondValue = condition.getSecondOperandValue();
break;
 
default:
throw new RuntimeException("Unknown second operand type: "
+ condition.getSecondOperandType());
}
 
// compare
switch(condition.getOperation()) {
case PathElement.OPERATION_EQUAL:
if(!firstValue.equals(secondValue)) return false;
break;
 
default:
throw new RuntimeException("Unknown operation: "
+ condition.getOperation());
}
}
 
return true;
}
 
public static void main(String[] args)
throws Exception
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.newDocument();
DocumentTree tree = new DocumentTree(document);
Transformer transformer
= TransformerFactory.newInstance().newTransformer();
 
// test update -------------------------------------------------------------
 
tree.updateDocument(
"/Vertrag/bbb@name:='BBB'",
null );
 
tree.updateDocument(
"/Vertrag/bbb@id:='ZZZ'",
null );
 
Element ccc = tree.updateElement(
"/Vertrag/bbb[@name='BBB' and @id=${attr1}]/ccc@test := $value",
new Object[] {
"value", null,
"attr1", "ZZZ"
} );
 
tree.updateElement(
"/Vertrag/bbb[@name='BBB' and @id=${attr1}]/ccc@test2 := $value",
new Object[] {
"value", "66666",
"attr1", "ZZZ"
} );
 
tree.updateElement(ccc,
"subccc# := 'test relative path'",
(Object[])null);
 
tree.updateElement(ccc,
"../../subnode@attr := 'test relative path 2'",
(Object[])null);
 
tree.updateDocument(
"/Vertrag/bbb/ddd# .= '123'",
null);
 
tree.updateDocument(
"/Vertrag/bbb/ddd# .= '456'",
null);
 
tree.updateDocument(
"/Vertrag/bbb/ccc[new]@test3 := $value",
new Object[] {
"value", "ABCD",
"attr1", "ZZZ"
} );
 
tree.updateDocument(
"/Vertrag/bbb/*# .= '!!!'",
null);
 
tree.updateDocument(
"/**@nnn := 'test last element'",
null);
 
tree.updateDocument(
"/**/../..@lll := 'test last element'",
null);
 
tree.updateDocument(
"/Vertrag/Sparten/Sparte[@bezeichnung=${a}]"
+ "/SpartenspezifischerTeil/Wagnis[@nummer=${c}]/Objekt[@nummer=${d}]",
new Object[] {
"a", "Gebaeude",
"c", "0001",
"d", "0002"
} );
 
tree.updateDocument(
"/Vertrag/Sparten/Sparte[@bezeichnung=${a}]"
+ "/SpartenspezifischerTeil/Wagnis[@nummer=${c}]/Objekt[@nummer=${d}]",
new Object[] {
"a", "Gebaeude",
"c", "0001",
"d", "0003"
} );
 
// test search -------------------------------------------------------------
 
tree.searchElement("/Vertrag/Sparten", (Object[])null);
tree.searchElement("/Vertrag2/Sparten", (Object[])null);
tree.searchElement("/Vertrag/nnnn/mmmm/mmm", (Object[])null);
 
// write result ------------------------------------------------------------
 
transformer.transform(new DOMSource(document), new StreamResult(
new FileOutputStream("result.xml")));
 
(new XmlSaver(new FileOutputStream("result2.xml"))).save(document);
}
}
/zpath/trunk/src/ak/zpath/PathCreateException.java
0,0 → 1,17
package ak.zpath;
 
import java.text.ParseException;
 
public class PathCreateException
extends ParseException
{
public PathCreateException(String message)
{
super(message, -1);
}
 
public PathCreateException(String message, Token token)
{
super(message, token == null ? -1 : token.getPosition());
}
}
/zpath/trunk/src/ak/zpath/Token.java
0,0 → 1,66
package ak.zpath;
 
public class Token
{
public static final int TOKEN_SLASH = 1;
public static final int TOKEN_OPEN_BRACKET = 2;
public static final int TOKEN_CLOSE_BRACKET = 3;
public static final int TOKEN_AT = 4;
public static final int TOKEN_SHARP = 5;
public static final int TOKEN_STRING = 6;
public static final int TOKEN_EQUAL = 7;
public static final int TOKEN_VARIABLE = 8;
public static final int TOKEN_ASSIGN = 9;
public static final int TOKEN_AND = 10;
public static final int TOKEN_OR = 11;
public static final int TOKEN_NEW = 12;
public static final int TOKEN_NAME = 13;
public static final int TOKEN_APPEND = 14;
 
private int type;
private int pos;
private String value;
 
public Token(int type, int pos, String value)
{
this.type = type;
this.pos = pos;
this.value = value;
}
 
public int getType()
{
return type;
}
 
public int getPosition()
{
return pos;
}
 
public String getValue()
{
return value;
}
 
public String toString()
{
switch(type) {
case Token.TOKEN_SLASH: return "slash";
case Token.TOKEN_OPEN_BRACKET: return "open bracket";
case Token.TOKEN_CLOSE_BRACKET: return "close bracket";
case Token.TOKEN_AT: return "at";
case Token.TOKEN_SHARP: return "sharp";
case Token.TOKEN_STRING: return "string [" + value + "]";
case Token.TOKEN_EQUAL: return "equal";
case Token.TOKEN_VARIABLE: return "variable [" + value + "]";
case Token.TOKEN_ASSIGN: return "assign";
case Token.TOKEN_APPEND: return "append";
case Token.TOKEN_AND: return "and";
case Token.TOKEN_OR: return "or";
case Token.TOKEN_NEW: return "new";
case Token.TOKEN_NAME: return "name [" + value + "]";
default: throw new RuntimeException("Unknown token type: " + type);
}
}
}
/zpath/trunk/src/ak/zpath/XmlSaver.java
0,0 → 1,129
package ak.zpath;
 
import org.w3c.dom.*;
import java.io.*;
 
public class XmlSaver
{
public static final String ENCODING = "UTF-8";
public static final String INDENT = "\t";
 
private OutputStream output;
private PrintStream printer;
 
public XmlSaver()
{
}
 
public XmlSaver(OutputStream output)
{
setOutput(output);
}
 
public OutputStream getOutput()
{
return output;
}
 
public void setOutput(OutputStream output)
{
this.output = output;
}
 
public void save(Document document)
throws IOException
{
printer = new PrintStream(output, false, ENCODING);
printer.println(
"<?xml version=\"1.0\" encoding=\"" + ENCODING + "\"?>\n");
 
if(document != null)
saveElement(document.getDocumentElement(), "");
 
printer.flush();
}
 
protected void saveElement(Element element, String indent)
throws IOException
{
if(element == null) return;
 
String newIndent = indent + INDENT;
NodeList children = element.getChildNodes();
NamedNodeMap attrs = element.getAttributes();
boolean isEmpty = true;
boolean hasText = false;
 
// print tag name
printer.print(indent);
printer.print("<");
printer.print(element.getTagName());
 
// print attributes
for(int i = 0; i < attrs.getLength(); i++) {
Attr attr = (Attr)attrs.item(i);
printer.print(" ");
printer.print(attr.getName());
printer.print("=\"");
printer.print(attr.getValue());
printer.print("\"");
}
 
// chech if has nested elements
for(int i = 0; i < children.getLength(); i++) {
Node subnode = children.item(i);
 
if(subnode instanceof Comment)
isEmpty = false;
else if(subnode instanceof Element)
isEmpty = false;
else if(subnode instanceof Text)
hasText = true;
}
 
if(isEmpty) { // empty element
if(!hasText) {
printer.println("/>");
}
else {
printer.print(">");
 
// print nested text
for(int i = 0; i < children.getLength(); i++) {
Node subnode = children.item(i);
if(subnode instanceof Text)
printer.print(subnode.getNodeValue());
}
 
printer.print("</");
printer.print(element.getTagName());
printer.println(">");
}
}
else { // has nested elements
printer.println(">");
 
// print nested elements, comment, text
for(int i = 0; i < children.getLength(); i++) {
Node subnode = children.item(i);
 
if(subnode instanceof Comment) {
printer.print("<!--");
printer.print(subnode.getNodeValue());
printer.println("-->");
}
else if(subnode instanceof Element) {
saveElement((Element)subnode, newIndent);
}
else if(subnode instanceof Text) {
printer.print(subnode.getNodeValue());
}
}
 
printer.print(indent);
printer.print("</");
printer.print(element.getTagName());
printer.println(">");
}
}
}
/zpath/trunk/src/ak/zpath/PathCondition.java
0,0 → 1,69
package ak.zpath;
 
public class PathCondition
{
private int firstOperandType;
private String firstOperandValue;
private int secondOperandType;
private String secondOperandValue;
private int operationType;
 
public int getFirstOperandType()
{
return firstOperandType;
}
 
public void setFirstOperandType(int operandType)
{
firstOperandType = operandType;
}
 
public String getFirstOperandValue()
{
return firstOperandValue;
}
 
public void setFirstOperandValue(String value)
{
firstOperandValue = value;
}
 
public int getOperation()
{
return operationType;
}
 
public void setOperation(int operationType)
{
this.operationType = operationType;
}
 
public int getSecondOperandType()
{
return secondOperandType;
}
 
public void setSecondOperandType(int operandType)
{
secondOperandType = operandType;
}
 
public String getSecondOperandValue()
{
return secondOperandValue;
}
 
public void setSecondOperandValue(String value)
{
secondOperandValue = value;
}
 
public void validateCondition()
{
}
 
public String toString()
{
return "Condition";
}
}
/zpath/trunk/src/ak/zpath/PathElement.java
0,0 → 1,130
package ak.zpath;
 
import java.util.List;
import java.util.ArrayList;
 
public class PathElement
{
public static final int OPERAND_NONE = 0;
public static final int OPERAND_TEXT = 1;
public static final int OPERAND_ATTRIBUTE = 2;
public static final int OPERAND_VARIABLE = 3;
public static final int OPERAND_STRING = 4;
public static final int OPERATION_ASSIGN = 5;
public static final int OPERATION_EQUAL = 6;
public static final int OPERATION_APPEND = 7;
 
private String name;
private boolean isNew = false;
private List conditions = new ArrayList();
private PathCondition condition;
private int firstOperandType = OPERAND_NONE;
private String firstOperandValue;
private int secondOperandType;
private String secondOperandValue;
private int operationType;
 
public String getName()
{
return name;
}
 
public void setName(String name)
{
this.name = name;
}
 
public int getFirstOperandType()
{
return firstOperandType;
}
 
public String getFirstOperandValue()
{
return firstOperandValue;
}
 
public void setFirstOperand(int operandType, String value)
{
firstOperandType = operandType;
firstOperandValue = value;
}
 
public int getOperation()
{
return operationType;
}
 
public void setOperation(int operationType)
{
this.operationType = operationType;
}
 
public int getSecondOperandType()
{
return secondOperandType;
}
 
public String getSecondOperandValue()
{
return secondOperandValue;
}
 
public void setSecondOperand(int operandType, String value)
{
secondOperandType = operandType;
secondOperandValue = value;
}
 
public void validateOperands()
{
}
 
public List getConditions()
{
return conditions;
}
 
public void addCondition()
{
condition = new PathCondition();
conditions.add(condition);
}
 
public void setFirstCondition(int operandType, String value)
{
condition.setFirstOperandType(operandType);
condition.setFirstOperandValue(value);
}
 
public void setConditionOperation(int operationType)
{
condition.setOperation(operationType);
}
 
public void setSecondCondition(int operandType, String value)
{
condition.setSecondOperandType(operandType);
condition.setSecondOperandValue(value);
}
 
public void validateCondition()
{
condition.validateCondition();
}
 
public boolean getNew()
{
return isNew;
}
 
public void setNew()
{
isNew = true;
}
 
public String toString()
{
return "Element [" + name + "]";
}
}
/zpath/trunk/src/ak/zpath/PathParseException.java
0,0 → 1,12
package ak.zpath;
 
import java.text.ParseException;
 
public class PathParseException
extends ParseException
{
public PathParseException(String message, int pos)
{
super(message, pos);
}
}
/zpath/trunk/src/ak/zpath/PathParser.java
0,0 → 1,224
package ak.zpath;
 
import java.util.List;
import java.util.ArrayList;
 
public class PathParser
{
public List parse(String path)
throws PathParseException
{
char c;
int startPos;
int pos = 0;
int endPos = path.length();
String token;
List tokens = new ArrayList();
 
while(pos < endPos) {
c = path.charAt(pos);
 
while(Character.isWhitespace(c) && pos < endPos - 1) {
c = path.charAt(++pos);
}
if(pos >= endPos) break;
 
switch(c) {
case '/':
addToken(tokens, pos, Token.TOKEN_SLASH, null);
pos++;
break;
 
case '[':
addToken(tokens, pos, Token.TOKEN_OPEN_BRACKET, null);
pos++;
break;
 
case ']':
addToken(tokens, pos, Token.TOKEN_CLOSE_BRACKET, null);
pos++;
break;
 
case '@':
addToken(tokens, pos, Token.TOKEN_AT, null);
pos++;
break;
 
case '#':
addToken(tokens, pos, Token.TOKEN_SHARP, null);
pos++;
break;
 
case '"':
case '\'':
startPos = pos+1;
pos = findStringEnd(path, c, startPos);
addToken(tokens, startPos-1, Token.TOKEN_STRING,
path.substring(startPos, pos));
pos++;
break;
 
case '=':
addToken(tokens, pos, Token.TOKEN_EQUAL, null);
pos++;
break;
 
case '$':
if(++pos >= endPos)
throw new PathParseException("variable name expected", pos);
c = path.charAt(pos);
if(c == '{') {
startPos = ++pos;
pos = findVariableEnd(path, c, startPos);
addToken(tokens, startPos-2,
Token.TOKEN_VARIABLE, path.substring(startPos, pos));
}
else if(Character.isLetter(c)) {
startPos = pos;
pos = findVariableEnd(path, '\u0000', pos);
addToken(tokens, startPos-1,
Token.TOKEN_VARIABLE, path.substring(startPos, pos));
}
else
throw new PathParseException("variable name expected", pos);
pos++;
break;
 
case ':':
if(++pos >= endPos)
throw new PathParseException("'=' expected", pos);
c = path.charAt(pos);
if(c == '=')
addToken(tokens, pos-1, Token.TOKEN_ASSIGN, null);
else
throw new PathParseException("'=' expected", pos);
pos++;
break;
 
case '.':
if(++pos >= endPos)
throw new PathParseException("'=' or '.' expected", pos);
c = path.charAt(pos);
if(c == '=')
addToken(tokens, pos-1, Token.TOKEN_APPEND, null);
else if(c == '.')
addToken(tokens, pos-1, Token.TOKEN_NAME, "..");
else
throw new PathParseException("'=' expected", pos);
pos++;
break;
 
case '*':
if(pos < endPos-1 && path.charAt(pos+1) == '*') {
addToken(tokens, pos, Token.TOKEN_NAME, "**");
pos += 2;
}
else {
addToken(tokens, pos, Token.TOKEN_NAME, "*");
pos++;
}
break;
 
default:
if(Character.isLetter(c) || c == '_') {
startPos = pos;
pos = findTokenEnd(path, pos);
token = path.substring(startPos, pos);
if("and".equals(token))
addToken(tokens, startPos, Token.TOKEN_AND, null);
else if("or".equals(token))
addToken(tokens, startPos, Token.TOKEN_OR, null);
else if("new".equals(token))
addToken(tokens, startPos, Token.TOKEN_NEW, null);
else
addToken(tokens, startPos, Token.TOKEN_NAME, token);
}
else
throw new PathParseException(
"unexpected character '" + c + "'", pos);
}
}
 
return tokens;
}
 
private int findStringEnd(String path, char c, int pos)
throws PathParseException
{
int endPos = path.length();
 
if(pos >= endPos - 1)
throw new PathParseException(
"unclosed string, '" + c + "' expected", pos);
 
while(path.charAt(++pos) != c) {
if(pos >= endPos - 1)
throw new PathParseException(
"unclosed string, '" + c + "' expected", pos);
}
 
return pos;
}
 
private int findVariableEnd(String path, char c, int pos)
throws PathParseException
{
int endPos = path.length();
 
if(pos >= endPos)
throw new PathParseException("unexpected end of variable name", pos);
 
char c2 = path.charAt(pos);
 
if(c == '{' && c2 == '}')
throw new PathParseException("empty variable name", pos);
else if(!Character.isLetter(c2) && c2 != '_')
throw new PathParseException("variable name expected", pos);
 
while(true) {
if(pos >= endPos - 1)
throw new PathParseException("unexpected end of variable name", pos);
 
c2 = path.charAt(++pos);
 
if(c == '{') {
if(c2 == '}') break;
if(!Character.isLetter(c2) && !Character.isDigit(c2) && c2 != '_')
throw new PathParseException("unexpected character '" + c2
+ "' in variable name is occured instead of '}'", pos);
}
else {
if(!Character.isLetter(c2) && !Character.isDigit(c2) && c2 != '_')
break;
if(pos >= endPos - 1) {
pos++;
break;
}
}
}
 
return pos;
}
 
private int findTokenEnd(String path, int pos)
throws PathParseException
{
int endPos = path.length();
char c;
 
while(true) {
pos++;
if(pos >= endPos) break;
 
c = path.charAt(pos);
if(!Character.isLetter(c) && !Character.isDigit(c) && c != '_') break;
}
 
return pos;
}
 
private void addToken(List tokens, int pos, int type, String value)
{
tokens.add(new Token(type, pos, value));
}
}
/zpath/trunk/build.xml
0,0 → 1,31
<project name="zpath" default="compile" basedir=".">
 
<!--property name="build.compiler" value="jikes"/-->
 
<property name="output.lib" location="output/zpath.jar"/>
<property name="src" location="src"/>
<property name="classes" location="classes"/>
<property name="classpath" value=""/>
 
<target name="compile">
<mkdir dir="${classes}" />
 
<javac
srcdir="${src}"
destdir="${classes}"
classpath="${classpath}:${classes}"
debug="on"
extdirs="lib"
/>
 
<jar destfile="${output.lib}" basedir="${classes}"/>
</target>
 
<target name="clean">
<delete dir="${classes}"/>
<delete file="${output.lib}"/>
</target>
 
<target name="all" depends="clean,compile" />
 
</project>
/zpath/trunk/output
Property changes:
Added: svn:ignore
+*
/zpath/trunk
Property changes:
Added: svn:ignore
+classes