/** * Returns the textual value of this node, transforms all escaped chars (e.g. <TT>\'i</TT> => * <TT>í</TT>) */ public String getTexTextValue() { String text = value; if ((text != null) && (text.trim().length() > 0)) { Enumeration e = bundle.getKeys(); while (e.hasMoreElements()) { String key = (String) e.nextElement(); if (text.indexOf(key) != -1) text = stringReplace(text, key, bundle.getString(key)); } } return text; }
/** * This is a light structure to store parsed ABC informations retrieved from a {@link * org.parboiled.Node} structure. * * <p>A AbcNode can have child(s), one parent, can search for ancestries and descendants (childs, * grand-childs...) * * <p>Informations it contains: * * <ul> * <li>a label (which approximatively correspond to a {@link abc.parser.AbcGrammar} method * <li>a value * <li>a position in {@link abc.parser.CharStreamPosition} object * <li>a parent * <li>zero, one or more child(s) * <li>zero, one or more parsing errors {@link abc.parser.AbcParseError} * </ul> */ public class AbcNode extends PositionableInCharStream { private static final long serialVersionUID = -8874823875159174349L; private static final AbcTextReplacements bundle = AbcTextReplacements.getInstance(); private List childs; private List errors; private String label; private AbcNode parent; private String value; // @SuppressWarnings("unchecked") protected AbcNode( Node node, InputBuffer parseInputBuffer, List<ParseError> parseErrors, AbcInputBuffer abcInputBuffer) { super(null); if (node != null) { this.label = node.getLabel(); this.value = parseInputBuffer.extract(node.getStartIndex(), node.getEndIndex()); Position pos = parseInputBuffer.getPosition(node.getStartIndex()); int sourceStartIndex = abcInputBuffer.getIndex(pos); int sourceEndIndex = sourceStartIndex + value.length(); setCharStreamPosition( new CharStreamPosition(pos.line, pos.column, sourceStartIndex, sourceEndIndex)); this.childs = new ArrayList(node.getChildren().size()); Iterator it = node.getChildren().iterator(); while (it.hasNext()) { AbcNode abcn = new AbcNode((Node) it.next(), parseInputBuffer, parseErrors, abcInputBuffer); abcn.parent = this; childs.add(abcn); } if (!hasError() /*(childs.size() == 0)*/ && node.hasError()) { this.errors = new ArrayList(); it = parseErrors.iterator(); while (it.hasNext()) { ParseError pe = (ParseError) it.next(); String peValue = pe.getInputBuffer().extract(pe.getStartIndex(), pe.getEndIndex()); String peMsg = pe.getErrorMessage(); Position pePos = pe.getInputBuffer().getPosition(pe.getStartIndex()); int peIndex = abcInputBuffer.getIndex(pePos); CharStreamPosition csp = new CharStreamPosition( pePos.line, pePos.column, peIndex, peIndex + (peValue.length() > 0 ? peValue.length() : 1)); // if ((pe.getStartIndex() >= node.getStartIndex()) // && (pe.getStartIndex() </*=*/ node.getEndIndex())) { if ((peIndex >= sourceStartIndex) && ((peIndex < sourceEndIndex) || (sourceStartIndex == sourceEndIndex))) { errors.add(new AbcParseError(peMsg, peValue, csp)); } } } } else { this.label = "AbcFile-Error"; this.value = ""; int nbL = parseInputBuffer.getLineCount(); for (int i = 1; i <= nbL; i++) this.value += parseInputBuffer.extractLine(i) + "\n"; this.childs = new ArrayList(0); setCharStreamPosition(new CharStreamPosition(1, 1, 0, 1)); if (parseErrors != null) { this.errors = new ArrayList(); Iterator it = parseErrors.iterator(); while (it.hasNext()) { ParseError pe = (ParseError) it.next(); errors.add(new AbcParseError(pe.getErrorMessage(), value, getCharStreamPosition())); } } } } /** * Returns the first child having the request label, <code>null</code> if doesn't exist. * * <p>Can get directly a grandchild if label contains <tt>/</tt> e.g. * abcHeader.getChild("FieldNumber/DIGITS") returns the grandchild "DIGITS" if exist in * FieldNumber child. * * @param label One of {@link AbcTokens} constants */ public AbcNode getChild(String label) { if (label == null || label.equals("")) return null; String[] generation = label.split("/"); String child = generation[0]; String grandchild = ""; if (generation.length > 1) { for (int i = 1; i < generation.length; i++) { grandchild += (grandchild.equals("") ? "" : "/"); grandchild += generation[i]; } } Iterator it = childs.iterator(); while (it.hasNext()) { AbcNode abcn = (AbcNode) it.next(); if (child.equals(abcn.getLabel())) { if (grandchild.equals("")) return abcn; else return abcn.getChild(grandchild); } } return null; } public List getChilds() { return childs; } /** * Returns a list of childs having the requested label * * <p>Can get directly grandchilds if label contains <tt>/</tt> e.g. * titleFields.getChild("FieldTitle/TexText") returns all grandchilds "TexText" in all * "FieldTitle" childs. */ public List getChilds(String label) { if (label == null || label.equals("")) return new ArrayList(0); String[] generation = label.split("/"); String child = generation[0]; String grandchild = ""; if (generation.length > 1) { for (int i = 1; i < generation.length; i++) { grandchild += (grandchild.equals("") ? "" : "/"); grandchild += generation[i]; } } List ret = new ArrayList(childs.size()); Iterator it = childs.iterator(); while (it.hasNext()) { AbcNode abcn = (AbcNode) it.next(); if (abcn.getLabel().equals(child)) { if (grandchild.equals("")) ret.add(abcn); else ret.addAll(abcn.getChilds(grandchild)); } } return ret; } /** * Look for child, grandchild, grand-grand-child having the requested label. When such one is * found, doesn't continue search into his own childs. * * @param label */ public List getChildsInAllGenerations(String label) { if (label == null || label.equals("")) return new ArrayList(0); List ret = new ArrayList(); Iterator it = childs.iterator(); while (it.hasNext()) { AbcNode abcn = (AbcNode) it.next(); if (abcn.getLabel().equals(label)) { ret.add(abcn); } else { ret.addAll(abcn.getChildsInAllGenerations(label)); } } return ret; } /** * Return the deepest childs of this node. This is useful to browse easily smallest segments of * parsed text, and get closer to errors. * * <p>e.g. node A has childs B and C.<br> * B has 3 childs D, E and F.<br> * A.getDeepestChilds() returns D, E, F, C in this order. * * <p>If it goes too deep, you can check if node is child of B or C using {@link * #isChildOf(String)} or {@link #isChildOf_or_is(String)}. * * @return a List of node */ public List getDeepestChilds() { if (childs.size() == 0) { return new ArrayList(0); } List ret = new ArrayList(childs.size() * 3); Iterator it = getChilds().iterator(); while (it.hasNext()) { AbcNode child = (AbcNode) it.next(); if (!child.hasChilds()) ret.add(child); else { ret.addAll(child.getDeepestChilds()); } } return ret; } /** Returns a List of errors in all childs, grand-childs... */ public List getErrors() { if (hasChilds()) { List ret = new ArrayList(0); Iterator it = getChilds().iterator(); while (it.hasNext()) { ret.addAll(((AbcNode) it.next()).getErrors()); } return ret; } else { return errors != null ? errors : new ArrayList(0); } } /** Returns the first child, <code>null</code> if no child */ public AbcNode getFirstChild() { if (hasChilds()) return (AbcNode) childs.get(0); else return null; } /** Returns the value parsed into integer if label is DIGIT or DIGITS, else -1. */ protected int getIntValue() { if (label.equals(AbcTokens.DIGIT) || label.equals(AbcTokens.DIGITS)) { try { return Integer.parseInt(value); } catch (NumberFormatException nfe) { return -1; } } else return -1; } /** Returns the node label (token name) */ public String getLabel() { return label; } /** Get direct parent, <code>null</code> if this node is the root. */ public AbcNode getParent() { return parent; } /** Returns the value parsed into short if label is DIGIT or DIGITS, else -1. */ protected short getShortValue() { if (label.equals(AbcTokens.DIGIT) || label.equals(AbcTokens.DIGITS)) { try { return Short.parseShort(value); } catch (NumberFormatException nfe) { return -1; } } else return -1; } /** * Returns the textual value of this node, transforms all escaped chars (e.g. <TT>\'i</TT> => * <TT>í</TT>) */ public String getTexTextValue() { String text = value; if ((text != null) && (text.trim().length() > 0)) { Enumeration e = bundle.getKeys(); while (e.hasMoreElements()) { String key = (String) e.nextElement(); if (text.indexOf(key) != -1) text = stringReplace(text, key, bundle.getString(key)); } } return text; } /** * Returns the textual value of this node * * <p>If this node is labelled "TexText", use {@link #getTexTextValue()} which transforms all * escaped chars (e.g. <TT>\'i</TT> => <TT>í</TT>) */ public String getValue() { return value; } /** * Returns a list of childs having the requested label and an non-empty value, and no error. * * <p>Can get directly grandchilds if label contains <tt>/</tt> e.g. * titleFields.getChild("FieldTitle/TexText") returns all grandchilds "TexText" in all * "FieldTitle" childs. */ public List getValuedChilds(String label) { List ret = getChilds(label); Iterator it = ret.iterator(); while (it.hasNext()) { AbcNode node = (AbcNode) it.next(); if (node.hasError() || (node.getValue() == null) || (node.getValue().length() == 0)) it.remove(); } return ret; } /** * Returns true if have the request label. * * <p>Can check directly a grandchild if label contains / e.g. * abcHeader.hasChild("FieldNumber/DIGITS") * * @param label One of {@link AbcTokens} constants * @return this is a shortcut of {@link #getChild(String)} != null */ public boolean hasChild(String label) { return getChild(label) != null; } public boolean hasChilds() { return childs.size() > 0; } public boolean hasError() { if (hasChilds()) { Iterator it = getChilds().iterator(); while (it.hasNext()) { if (((AbcNode) it.next()).hasError()) return true; } return false; } else { return errors != null ? (errors.size() > 0) : false; } } /** Check if this node has parent, i.e. is not the root of the structure */ public boolean hasParent() { return parent != null; } /** * Checks if the AbcNode is labelled with requested token * * <p>This is a shortcut to getLabel().equals("MyLabel") * * @param label One of {@link AbcTokens} constants */ public boolean is(String label) { if (label != null) return label.equals(getLabel()); else return false; } /** * If has parent, checks if the parent node is labelled with requested token. i.e. checks if this * node is the child, grandchild... of a node labelled <TT>label</TT> * * @param label One of {@link AbcTokens} constants */ public boolean isChildOf(String label) { if (label == null) return false; else { if (hasParent()) { if (getParent().is(label)) return true; else return getParent().isChildOf(label); } else { return false; } } } /** * This is a shortcut for {@link #is(String)} || {@link #isChildOf(String)}. * * <p>Checks if this node or one of its ancestors is labelled <TT>label</TT> * * @param label One of {@link AbcTokens} constants */ public boolean isChildOf_or_is(String label) { return is(label) || isChildOf(label); } /** String.replace(String arg0, String arg1) in java 1.5 */ private String stringReplace(String text, String target, String replacement) { return Pattern.compile(target.toString(), Pattern.LITERAL) .matcher(text) .replaceAll(replacement); } // @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append('['); sb.append(getLabel()); sb.append(']'); if (hasError()) sb.append('E'); return StringUtils.escape(sb.toString()); } }