001: /*
002: * Created on Aug 4, 2005
003: */
004: package uk.org.ponder.beanutil;
005:
006: import uk.org.ponder.stringutil.CharWrap;
007: import uk.org.ponder.stringutil.StringList;
008:
009: /**
010: * A set of utility methods to operate on dot-separated bean paths.
011: *
012: * @author Antranig Basman (antranig@caret.cam.ac.uk)
013: *
014: */
015: public class PathUtil {
016:
017: public static String getHeadPath(String path) {
018: return getPathSegment(path, 0);
019: }
020:
021: /** Returns the first path segment, without performing unescaping **/
022: public static String getHeadPathEncoded(String path) {
023: int firstdot = getPathSegment(null, path, 0);
024: return path.substring(0, firstdot);
025: }
026:
027: public static String getFromHeadPath(String path) {
028: int firstdot = getPathSegment(null, path, 0);
029: return firstdot == path.length() ? null : path
030: .substring(firstdot + 1);
031: }
032:
033: public static String getToTailPath(String path) {
034: int lastdot = lastDotIndex(path);
035: return lastdot == -1 ? null : path.substring(0, lastdot);
036: }
037:
038: /** Returns the very last path component of a bean path */
039: public static String getTailPath(String path) {
040: int lastdot = lastDotIndex(path);
041: return getPathSegment(path, lastdot + 1);
042: }
043:
044: /** Parses a path into an array of decoded EL segments **/
045: public static String[] splitPath(String path) {
046: StringList togo = new StringList();
047: CharWrap build = new CharWrap();
048: int index = 0;
049: while (index < path.length()) {
050: index = PathUtil.getPathSegment(build, path, index) + 1;
051: togo.add(build.toString());
052: build.clear();
053: }
054: return togo.toStringArray();
055: }
056:
057: /** Builds an EL path of from an array of path segments. Particulary good when using
058: * Strings of BeanLocators, Maps, and friends. Assumes none of the segments
059: * have been escaped yet.
060: */
061: public static String buildPath(String[] segments) {
062: return buildPath(segments, 0, segments.length);
063: }
064:
065: /** Builds an EL path of variable length, from a subsection of an array of
066: * segments.
067: */
068: public static String buildPath(String[] segments, int start,
069: int finish) {
070: CharWrap toappend = new CharWrap();
071: for (int i = start; i < finish; i++) {
072: if (toappend.size != 0) {
073: toappend.append('.');
074: }
075: composeSegment(toappend, segments[i]);
076: }
077: return toappend.toString();
078: }
079:
080: /**
081: * Compose a prefix and suffix EL path, where the prefix is already escaped.
082: * Prefix may be empty, but not null. The suffix will become escaped.
083: */
084: public static String composePath(String prefix, String suffix) {
085: CharWrap toappend = new CharWrap(prefix);
086: if (toappend.size != 0) {
087: toappend.append('.');
088: }
089: composeSegment(toappend, suffix);
090: return toappend.toString();
091: }
092:
093: /** Compose head and tail paths, where escaping is unnecessary * */
094: public static String composePathEncoded(String head, String tail) {
095: return head + '.' + tail;
096: }
097:
098: /**
099: * Compose a prefix and suffix EL path, where the prefix has not been escaped,
100: * and is not null.
101: * @param prefix A single path segment (bean name) starting the path, may not
102: * be null or empty. This will become escaped.
103: * @param suffix A single path segment (property name) continuing the path,
104: * may not be null or empty. This will become escaped.
105: * @return A properly escaped full path, representing "prefix.suffix".
106: */
107: public static String buildPath(String prefix, String suffix) {
108: CharWrap toappend = new CharWrap();
109: composeSegment(toappend, prefix);
110: toappend.append('.');
111: composeSegment(toappend, suffix);
112: return toappend.toString();
113: }
114:
115: static int lastDotIndex(String path) {
116: for (int i = path.length() - 1; i >= 0; --i) {
117: if (path.charAt(i) == '.'
118: && (i == 0 || path.charAt(i) != '\\'))
119: return i;
120: }
121: return -1;
122: }
123:
124: static void composeSegment(CharWrap toaccept, String toappend) {
125: for (int i = 0; i < toappend.length(); ++i) {
126: char c = toappend.charAt(i);
127: if (c == '.' || c == '\\' || c == '}') {
128: toaccept.append('\\').append(c);
129: } else
130: toaccept.append(c);
131: }
132: }
133:
134: static String getPathSegment(String path, int i) {
135: CharWrap accept = new CharWrap();
136: getPathSegment(accept, path, i);
137: return accept.toString();
138: }
139:
140: static int getPathSegment(CharWrap accept, String path, int i) {
141: boolean escaped = false;
142: for (; i < path.length(); ++i) {
143: char c = path.charAt(i);
144: if (!escaped) {
145: if (c == '.') {
146: return i;
147: } else if (c == '\\') {
148: escaped = true;
149: } else if (accept != null)
150: accept.append(c);
151: } else {
152: escaped = false;
153: if (accept != null)
154: accept.append(c);
155: }
156: }
157: return i;
158: }
159:
160: }
|