001: /*
002: * Copyright (C) 2004, 2005, 2006 Joe Walnes.
003: * Copyright (C) 2006, 2007 XStream Committers.
004: * All rights reserved.
005: *
006: * The software in this package is published under the terms of the BSD
007: * style license a copy of which has been included with this distribution in
008: * the LICENSE.txt file.
009: *
010: * Created on 07. March 2004 by Joe Walnes
011: */
012: package com.thoughtworks.xstream.io.path;
013:
014: import java.util.HashMap;
015: import java.util.Map;
016:
017: /**
018: * Maintains the current {@link Path} as a stream is moved through.
019: *
020: * <p>Can be linked to a <a href="../HierarchicalStreamWriter.html">HierarchicalStreamWriter</a> or
021: * <a href="../HierarchicalStreamReader.html">HierarchicalStreamReader</a> by wrapping them with a
022: * <a href="PathTrackingWriter.html">PathTrackingWriter</a> or
023: * <a href="PathTrackingReader.html">PathTrackingReader</a>.</p>
024: *
025: * <h3>Example</h3>
026: *
027: * <pre>
028: * PathTracker tracker = new PathTracker();
029: * tracker.pushElement("table");
030: * tracker.pushElement("tr");
031: * tracker.pushElement("td");
032: * tracker.pushElement("form");
033: * tracker.popElement("form");
034: * tracker.popElement("td");
035: * tracker.pushElement("td");
036: * tracker.pushElement("div");
037: *
038: * Path path = tracker.getPath(); // returns "/table/tr/td[2]/div"
039: * </pre>
040: *
041: * @see Path
042: * @see PathTrackingReader
043: * @see PathTrackingWriter
044: *
045: * @author Joe Walnes
046: */
047: public class PathTracker {
048:
049: private int pointer;
050: private int capacity;
051: private String[] pathStack;
052: private Map[] indexMapStack;
053:
054: private Path currentPath;
055:
056: public PathTracker() {
057: this (16);
058: }
059:
060: /**
061: * @param initialCapacity Size of the initial stack of nodes (one level per depth in the tree). Note that this is
062: * only for optimizations - the stack will resize itself if it exceeds its capacity. If in doubt,
063: * use the other constructor.
064: */
065: public PathTracker(int initialCapacity) {
066: this .capacity = Math.max(1, initialCapacity);
067: pathStack = new String[capacity];
068: indexMapStack = new Map[capacity];
069: }
070:
071: /**
072: * Notify the tracker that the stream has moved into a new element.
073: *
074: * @param name Name of the element
075: */
076: public void pushElement(String name) {
077: if (pointer + 1 >= capacity) {
078: resizeStacks(capacity * 2);
079: }
080: pathStack[pointer] = name;
081: Map indexMap = indexMapStack[pointer];
082: if (indexMap == null) {
083: indexMap = new HashMap();
084: indexMapStack[pointer] = indexMap;
085: }
086: if (indexMap.containsKey(name)) {
087: indexMap.put(name, new Integer(((Integer) indexMap
088: .get(name)).intValue() + 1));
089: } else {
090: indexMap.put(name, new Integer(1));
091: }
092: pointer++;
093: currentPath = null;
094: }
095:
096: /**
097: * Notify the tracker that the stream has moved out of an element.
098: */
099: public void popElement() {
100: indexMapStack[pointer] = null;
101: currentPath = null;
102: pointer--;
103: }
104:
105: /**
106: * @deprecated Use {@link #getPath()} instead.
107: */
108: public String getCurrentPath() {
109: return getPath().toString();
110: }
111:
112: private void resizeStacks(int newCapacity) {
113: String[] newPathStack = new String[newCapacity];
114: Map[] newIndexMapStack = new Map[newCapacity];
115: int min = Math.min(capacity, newCapacity);
116: System.arraycopy(pathStack, 0, newPathStack, 0, min);
117: System.arraycopy(indexMapStack, 0, newIndexMapStack, 0, min);
118: pathStack = newPathStack;
119: indexMapStack = newIndexMapStack;
120: capacity = newCapacity;
121: }
122:
123: /**
124: * Current Path in stream.
125: */
126: public Path getPath() {
127: if (currentPath == null) {
128: String[] chunks = new String[pointer + 1];
129: chunks[0] = "";
130: for (int i = 0; i < pointer; i++) {
131: Integer integer = ((Integer) indexMapStack[i]
132: .get(pathStack[i]));
133: int index = integer.intValue();
134: if (index > 1) {
135: StringBuffer chunk = new StringBuffer(pathStack[i]
136: .length() + 6);
137: chunk.append(pathStack[i]).append('[')
138: .append(index).append(']');
139: chunks[i + 1] = chunk.toString();
140: } else {
141: chunks[i + 1] = pathStack[i];
142: }
143: }
144: currentPath = new Path(chunks);
145: }
146: return currentPath;
147: }
148: }
|