001: package org.objectweb.celtix.bus.configuration.spring;
002:
003: import java.util.ArrayList;
004: import java.util.Collections;
005: import java.util.Comparator;
006: import java.util.Iterator;
007: import java.util.List;
008: import java.util.ListIterator;
009:
010: import org.objectweb.celtix.configuration.Configuration;
011:
012: class BeanName {
013:
014: static final char LOOSE_BINDING = '*';
015: static final char TIGHT_BINDING = '.';
016: static final char NAMESPACE_URI_OPEN = '{';
017: static final char NAMESPACE_URI_CLOSE = '}';
018:
019: static final String ANY_COMPONENT = "?";
020:
021: String name;
022: String normalisedName;
023: ComponentIterator iterator;
024:
025: BeanName(String n) {
026: this (n, false);
027: }
028:
029: BeanName(String n, boolean doNormalise) {
030: name = n;
031: normalisedName = null;
032: if (doNormalise) {
033: normalise();
034: }
035: }
036:
037: BeanName(Configuration conf) {
038: StringBuffer buf = new StringBuffer();
039: Configuration c = conf;
040: while (null != c) {
041: if (buf.length() > 0) {
042: buf.insert(0, TIGHT_BINDING);
043: }
044: buf.insert(0, c.getId().toString());
045: c = c.getParent();
046: }
047: name = buf.toString();
048: normalisedName = name;
049: }
050:
051: public String toString() {
052: return name;
053: }
054:
055: int compareTo(BeanName other, String component) {
056:
057: int result = 0;
058:
059: // An entry that contains a matching component (whether name or "?")
060: // takes precedence over entries that elide the level (that is, entries
061: // that match the level in a loose binding).
062:
063: char lb = iterator.lastBinding();
064: char olb = other.iterator.lastBinding();
065:
066: if (matchCurrentComponentName(component)) {
067: if (!other.matchCurrentComponentName(component)
068: && LOOSE_BINDING == olb) {
069: result = -1;
070: }
071: } else if (other.matchCurrentComponentName(component)
072: && LOOSE_BINDING == lb) {
073: return 1;
074: }
075:
076: if (0 != result) {
077: return result;
078: }
079:
080: // An entry with a matching name takes precedence over
081: // entries that match using "?".
082:
083: String c = iterator.current;
084: String oc = other.iterator.current;
085:
086: if (ANY_COMPONENT.equals(c)) {
087: if (!ANY_COMPONENT.equals(oc)) {
088: result = 1;
089: }
090: } else {
091: if (ANY_COMPONENT.equals(oc)) {
092: result = -1;
093: }
094: }
095: if (0 != result) {
096: return result;
097: }
098:
099: // An entry preceded by a tight binding takes precedence
100: // over entries preceded by a loose binding.
101:
102: if (isTightBinding(lb) && isLooseBinding(olb)) {
103: result = -1;
104: } else if (isLooseBinding(lb) && isTightBinding(olb)) {
105: result = 1;
106: }
107: return result;
108: }
109:
110: String getName() {
111: return name;
112: }
113:
114: String getNormalisedName() {
115: return normalisedName;
116: }
117:
118: BeanName findBestMatch(List<BeanName> candidateBeans) {
119:
120: List<BeanName> candidates = new ArrayList<BeanName>(
121: candidateBeans);
122:
123: for (BeanName bn : candidates) {
124: if (name.equals(bn.name)) {
125: return bn;
126: }
127: bn.normalise();
128: bn.reset();
129: if (bn.iterator.hasNext()) {
130: bn.iterator.next();
131: }
132: }
133:
134: normalise();
135: reset();
136:
137: while (iterator.hasNext() && candidates.size() > 0) {
138:
139: iterator.next();
140:
141: // at each level:
142:
143: // remove the non matching candidates
144:
145: for (int i = candidates.size() - 1; i >= 0; i--) {
146: BeanName candidate = candidates.get(i);
147: if (!match(candidate)) {
148: candidates.remove(i);
149: }
150: }
151:
152: // sort the remainder - using a comparator specific to the current level
153:
154: Comparator<BeanName> comparator = new Comparator<BeanName>() {
155: public int compare(BeanName o1, BeanName o2) {
156: return o1.compareTo(o2, iterator.current);
157: }
158: };
159: Collections.sort(candidates, comparator);
160:
161: // keep only the ties
162:
163: int i = 1;
164: while (i < candidates.size()) {
165: int diff = candidates.get(0).compareTo(
166: candidates.get(i), iterator.current);
167: if (diff < 0) {
168: break;
169: }
170: i++;
171: }
172: while (i < candidates.size()) {
173: candidates.remove(candidates.size() - 1);
174: }
175:
176: // advance remaining candidate iterators where necessary,
177: // pruning the list if necessary
178:
179: ListIterator<BeanName> it = candidates.listIterator();
180: BeanName candidate = it.hasNext() ? it.next() : null;
181: while (null != candidate) {
182: BeanName nextCandidate = it.hasNext() ? it.next()
183: : null;
184: char lb = candidate.iterator.lastBinding();
185: if (0 == lb || isTightBinding(lb)) {
186: if (candidate.iterator.hasNext()) {
187: candidate.iterator.next();
188: } else if (iterator.hasNext()) {
189: candidates.remove(candidate);
190: }
191: } else if (matchCurrentComponentName(candidate)) {
192: assert isLooseBinding(lb);
193: if (candidate.iterator.hasNext()) {
194: candidate.iterator.next();
195: } else if (iterator.hasNext()) {
196: candidates.remove(candidate);
197: }
198: }
199: candidate = nextCandidate;
200: }
201: }
202: if (candidates.size() > 0) {
203: return candidates.get(0);
204: }
205: return null;
206: }
207:
208: boolean match(BeanName other) {
209: char olb = other.iterator.lastBinding();
210: if (isLooseBinding(olb)) {
211: return true;
212: } else {
213: return matchCurrentComponentName(other);
214: }
215: }
216:
217: boolean matchCurrentComponentName(BeanName other) {
218: return iterator.current.equals(other.iterator.current)
219: || ANY_COMPONENT.equals(other.iterator.current);
220: }
221:
222: boolean matchCurrentComponentName(String component) {
223: return component.equals(iterator.current)
224: || ANY_COMPONENT.equals(iterator.current);
225: }
226:
227: /**
228: * Replace contiguous sequences of two or more binding characters by
229: * a single tight binding if the sequence contains only tight binding characters,
230: * or by a loose binding character otherwise.
231: * @param beanName
232: * @return
233: */
234:
235: final void normalise() {
236: if (null != normalisedName) {
237: return;
238: }
239: if (null == name || name.length() < 2) {
240: normalisedName = name;
241: return;
242: }
243: StringBuffer buf = new StringBuffer();
244:
245: int i = 0;
246: int from = 0;
247: while (i < name.length()) {
248: boolean inNamespaceURI = false;
249: from = i;
250: if (isBinding(name.charAt(i))) {
251: while (i < name.length() && isBinding(name.charAt(i))) {
252: i++;
253: }
254: if (i - from > 1) {
255: int pos = name.indexOf(LOOSE_BINDING, from);
256: if (pos >= 0 && pos <= i) {
257: buf.append(LOOSE_BINDING);
258:
259: } else {
260: buf.append(TIGHT_BINDING);
261: }
262: } else {
263: buf.append(name.charAt(from));
264: }
265:
266: } else {
267: do {
268: buf.append(name.charAt(i));
269: if (NAMESPACE_URI_OPEN == name.charAt(i)
270: && !inNamespaceURI) {
271: inNamespaceURI = true;
272: } else if (NAMESPACE_URI_CLOSE == name.charAt(i)
273: && inNamespaceURI) {
274: inNamespaceURI = false;
275: }
276: i++;
277: } while (i < name.length()
278: && (!isBinding(name.charAt(i)) || inNamespaceURI));
279: }
280: }
281:
282: normalisedName = buf.toString();
283: }
284:
285: final void reset() {
286: iterator = new ComponentIterator();
287: }
288:
289: final ComponentIterator getIterator() {
290: if (null == iterator) {
291: iterator = new ComponentIterator();
292: }
293: return iterator;
294: }
295:
296: static boolean isBinding(char c) {
297: return isTightBinding(c) || isLooseBinding(c);
298: }
299:
300: static boolean isTightBinding(char c) {
301: return TIGHT_BINDING == c;
302: }
303:
304: static boolean isLooseBinding(char c) {
305: return LOOSE_BINDING == c;
306: }
307:
308: final class ComponentIterator implements Iterator {
309: int componentStart = -1;
310: int componentEnd = -1;
311: String current;
312:
313: public boolean hasNext() {
314: if (null == normalisedName) {
315: return false;
316: }
317: return componentEnd + 1 < normalisedName.length();
318: }
319:
320: public Object next() {
321: if (-1 == componentStart && -1 == componentEnd
322: && isBinding(normalisedName.charAt(0))) {
323: componentStart++;
324: componentEnd++;
325: }
326: componentStart = componentEnd + 1;
327: componentEnd = componentStart + 1;
328: boolean inNamespaceURI = false;
329: if (componentStart < normalisedName.length()
330: && NAMESPACE_URI_OPEN == normalisedName
331: .charAt(componentStart)) {
332: inNamespaceURI = true;
333: }
334:
335: while (componentEnd < normalisedName.length()) {
336: if (NAMESPACE_URI_OPEN == normalisedName
337: .charAt(componentEnd)
338: && !inNamespaceURI) {
339: inNamespaceURI = true;
340: } else if (NAMESPACE_URI_CLOSE == normalisedName
341: .charAt(componentEnd)
342: && inNamespaceURI) {
343: inNamespaceURI = false;
344: }
345: if (!inNamespaceURI
346: && isBinding(normalisedName
347: .charAt(componentEnd))) {
348: break;
349: }
350: componentEnd++;
351: }
352:
353: /*
354: while (componentEnd < normalisedName.length()
355: && !isBinding(normalisedName.charAt(componentEnd))) {
356: componentEnd++;
357: }
358: */
359: current = normalisedName.substring(componentStart,
360: componentEnd);
361: return current;
362: }
363:
364: public void remove() {
365: // TODO Auto-generated method stub
366: }
367:
368: char lastBinding() {
369: if (componentStart > 0) {
370: return normalisedName.charAt(componentStart - 1);
371: }
372: return 0;
373: }
374:
375: }
376: }
|