001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package javax.naming;
019:
020: import java.io.IOException;
021: import java.io.ObjectInputStream;
022: import java.io.ObjectOutputStream;
023: import java.io.OptionalDataException;
024: import java.util.Enumeration;
025: import java.util.List;
026: import java.util.Vector;
027:
028: import org.apache.harmony.jndi.internal.nls.Messages;
029:
030: /**
031: * A <code>CompositeName</code> represents a name in a naming service which
032: * spans multiple namespaces. For example the name "www.eclipse.org/index.html"
033: * spans the DNS and file system namespaces.
034: * <p>
035: * A <code>CompositeName</code> is a series of string elements. A composite
036: * name has a sequence of zero or more elements delimited by the '/' char. Each
037: * element can be accessed using its position. The first element is at position
038: * 0.
039: * </p>
040: * <p>
041: * A <code>CompositeName</code> may be empty. An empty composite name has no
042: * elements. Elements may also be empty.
043: * </p>
044: * <p>
045: * <code>CompositeName</code>s are read from left to right unlike
046: * <code>CompoundName</code>s which may have their direction of ordering
047: * specified by properties.
048: * </p>
049: * <p>
050: * Special characters are as follows:
051: * </p>
052: * <ul>
053: * <li>The separator is /</li>
054: * <li>The escape character is \</li>
055: * <li>Quotes can be used - both single quotes and double quotes are allowed.
056: * This allows you to quote strings which contain chars such as / which are part
057: * of a <code>CompositeName</code> element to avoid them being read as a
058: * separator.</li>
059: * </ul>
060: * <p>
061: * See the examples for further clarification.
062: * </p>
063: * <p>
064: * Some Examples:<br />
065: * ==============
066: * </p>
067: * <p>
068: * The composite name "www.eclipse.org/index.html" has 2 elements.
069: * "www.eclipse.org" is a name from the DNS namespace. "index.html" is a name
070: * from the file system namespace.
071: * </p>
072: * <p>
073: * Another example of a composite name is: "www.eclipse.org/org/index.html".
074: * This name has 3 elements "www.eclipse.org", "org" and "index.html".
075: * www.eclipse.org is a name from the DNS namespace. The last 2 elements are
076: * each from the file system namespace.
077: * </p>
078: * <p>
079: * Some more examples to clarify empty names and elements:
080: * </p>
081: * <p>
082: * An empty CompositeName is the name "" and has no elements.
083: * </p>
084: * <p>
085: * A CompositeName with just one empty element is the name "/".
086: * </p>
087: * <p>
088: * The name "/org/" has 3 elements. The first and last are empty.
089: * </p>
090: * <p>
091: * The name "/a" has 2 elements. The first element is empty and the second
092: * element is "a".
093: * </p>
094: * <p>
095: * The name "a//a" has 3 elements. The middle element is empty and the first &
096: * third elements are both "a".
097: * </p>
098: * <p>
099: * The name "a/'b/a" is invalid as there is no closing quote for the '
100: * character.
101: * </p>
102: * <p>
103: * The name "a/'a/b/b" is invalid as there is no closing quote for the '
104: * character.
105: * </p>
106: * <p>
107: * The name "a/\"b/a" is interpreted as a/"b/a and is invalid as there is no
108: * closing quote for the embedded escaped " character.
109: * </p>
110: * <p>
111: * The name "a/'b/c'/a" has 3 elements. The middle element is b/c.
112: * <p>
113: * The name "a/a'a/b'/b" has 4 elements: Element 0 is "a". Element 1 is "a'a".
114: * Element 2 is "b'". Element 3 is "b".
115: * </p>
116: * <p>
117: * Interestingly the name "a/a'a/b/b" is valid and has 4 elements. This is
118: * because the single quote char ' is not a leading quote and is embedded in an
119: * element so is treated as a character. Element 0 is "a". Element 1 is "a'a".
120: * Element 2 is "b". Element 3 is "b".
121: * </p>
122: * <p>
123: * The name "\"abcd" gives an <code>InvalidNameException</code> as there is no
124: * closing quote.
125: * </p>
126: * <p>
127: * The name "'\"abcd'" gives one element of value "abcd.
128: * </p>
129: * <p>
130: * The name "\\abcd" gives one element of value \abcd.
131: * </p>
132: * <p> "" is empty. It has no elements. "/" has one empty element. "//" has 2
133: * empty elements. "/a/" has 3 elements the middle one is set to a. "///" has 3
134: * empty elements. "//a/" has 4 elements, the last but one is set to a.
135: * </p>
136: */
137: public class CompositeName implements Name {
138:
139: private static final long serialVersionUID = 1667768148915813118L;
140:
141: // status used by parse()
142: private static final int OUT_OF_QUOTE = 0;
143:
144: private static final int IN_SINGLE_QUOTE = 1;
145:
146: private static final int IN_DOUBLE_QUOTE = 2;
147:
148: private static final int QUOTE_ENDED = 3;
149:
150: /* a list holding elements */
151: private transient Vector<String> elems;
152:
153: /**
154: * Private copy constructor.
155: *
156: * @param elements
157: * a list of name elements
158: */
159: private CompositeName(List<String> elements) {
160: super ();
161: elems = new Vector<String>(elements);
162: }
163:
164: /**
165: * Construct a composite name with given elements.
166: *
167: * @param elements
168: * an enumeration of name elements
169: */
170: protected CompositeName(Enumeration<String> elements) {
171: super ();
172: elems = new Vector<String>();
173: while (elements.hasMoreElements()) {
174: elems.add(elements.nextElement());
175: }
176: }
177:
178: /**
179: * Default constructor, creates an empty name with zero elements.
180: */
181: public CompositeName() {
182: super ();
183: elems = new Vector<String>();
184: }
185:
186: /**
187: * This constructor takes the supplied name and breaks it down into its
188: * elements.
189: *
190: * @param name
191: * a string containing the full composite name
192: * @throws InvalidNameException
193: * if the supplied name is invalid
194: */
195: public CompositeName(String name) throws InvalidNameException {
196: super ();
197: elems = parseName(name);
198: }
199:
200: /**
201: * Parse string name elements. Delimiter is "/". Escape is "\" and both
202: * single quote and double quote are supported.
203: */
204: private static Vector<String> parseName(String name)
205: throws InvalidNameException {
206:
207: Vector<String> l = new Vector<String>();
208:
209: // special case: all '/', means same number of empty elements
210: if (isAllSlash(name)) {
211: for (int i = 0; i < name.length(); i++) {
212: l.add(""); //$NON-NLS-1$
213: }
214: return l;
215: }
216:
217: // general simple case, without escape and quote
218: if (name.indexOf('"') < 0 && name.indexOf('\'') < 0
219: && name.indexOf('\\') < 0) {
220: int i = 0, j = 0;
221: while ((j = name.indexOf('/', i)) >= 0) {
222: l.add(name.substring(i, j));
223: i = j + 1;
224: }
225: l.add(name.substring(i));
226: return l;
227: }
228:
229: // general complicated case, consider escape and quote
230: char c;
231: char chars[] = name.toCharArray();
232: StringBuilder buf = new StringBuilder();
233: int status = OUT_OF_QUOTE;
234: for (int i = 0; i < chars.length; i++) {
235: c = chars[i];
236:
237: // check end quote violation
238: if (status == QUOTE_ENDED) {
239: if (c == '/') {
240: l.add(buf.toString());
241: buf.setLength(0);
242: status = OUT_OF_QUOTE;
243: continue;
244: }
245: // jndi.0C=End quote is not at the end of element
246: throw new InvalidNameException(Messages
247: .getString("jndi.0C")); //$NON-NLS-1$
248: }
249:
250: if (c == '\\') {
251: // escape char
252: try {
253: char nc = chars[++i];
254: if (nc == '\\' || nc == '\'' || nc == '"'
255: || nc == '/') {
256: buf.append(nc);
257: } else {
258: buf.append(c);
259: buf.append(nc);
260: }
261: } catch (ArrayIndexOutOfBoundsException e) {
262: // jndi.0D=Escape cannot be at the end of element
263: throw new InvalidNameException(Messages
264: .getString("jndi.0D")); //$NON-NLS-1$
265: }
266: continue;
267: }
268: if (c != '/' && c != '"' && c != '\'') {
269: // normal char
270: buf.append(c);
271: continue;
272: }
273:
274: // special char
275: if (status == OUT_OF_QUOTE && c == '/') {
276: l.add(buf.toString());
277: buf.setLength(0);
278: } else if (status == OUT_OF_QUOTE && c == '\''
279: && buf.length() == 0) {
280: status = IN_SINGLE_QUOTE;
281: } else if (status == OUT_OF_QUOTE && c == '"'
282: && buf.length() == 0) {
283: status = IN_DOUBLE_QUOTE;
284: } else if (status == IN_SINGLE_QUOTE && c == '\'') {
285: status = QUOTE_ENDED;
286: } else if (status == IN_DOUBLE_QUOTE && c == '"') {
287: status = QUOTE_ENDED;
288: } else {
289: buf.append(c);
290: }
291: }
292: l.add(buf.toString());
293:
294: // check end status
295: if (status != OUT_OF_QUOTE && status != QUOTE_ENDED) {
296: // jndi.0E=Wrong quote usage.
297: throw new InvalidNameException(Messages
298: .getString("jndi.0E")); //$NON-NLS-1$
299: }
300: return l;
301: }
302:
303: private static boolean isAllSlash(String name) {
304: for (int i = 0; i < name.length(); i++) {
305: if (name.charAt(i) != '/') {
306: return false;
307: }
308: }
309: return true;
310: }
311:
312: /*
313: * Format name elements to its string representation.
314: */
315: private static String formatName(Vector<String> elems) {
316:
317: // special case: all empty elements
318: if (isAllEmptyElements(elems)) {
319: StringBuilder buf = new StringBuilder();
320: for (int i = 0; i < elems.size(); i++) {
321: buf.append("/"); //$NON-NLS-1$
322: }
323: return buf.toString();
324: }
325:
326: // general case
327: StringBuilder buf = new StringBuilder();
328: for (int i = 0; i < elems.size(); i++) {
329: String elem = elems.get(i);
330: if (i > 0) {
331: buf.append("/"); //$NON-NLS-1$
332: }
333: if (elem.indexOf('/') < 0 && elem.indexOf('\\') < 0
334: && elem.indexOf('\'') < 0 && elem.indexOf('"') < 0) {
335: buf.append(elem);
336: } else {
337: char chars[] = elem.toCharArray();
338: for (char c : chars) {
339: if (c == '/' || c == '\\' || c == '\'' || c == '"') {
340: buf.append('\\');
341: }
342: buf.append(c);
343: }
344: }
345: }
346: return buf.toString();
347: }
348:
349: private static boolean isAllEmptyElements(Vector<String> elems) {
350: for (int i = 0; i < elems.size(); i++) {
351: String elem = elems.get(i);
352: if (elem.length() > 0) {
353: return false;
354: }
355: }
356: return true;
357: }
358:
359: public Enumeration<String> getAll() {
360: return elems.elements();
361: }
362:
363: public String get(int index) {
364: return elems.get(index);
365: }
366:
367: public Name getPrefix(int index) {
368: if (index < 0 || index > elems.size()) {
369: throw new ArrayIndexOutOfBoundsException();
370: }
371: return new CompositeName(elems.subList(0, index));
372: }
373:
374: public Name getSuffix(int index) {
375: if (index < 0 || index > elems.size()) {
376: throw new ArrayIndexOutOfBoundsException();
377: }
378: return new CompositeName(elems.subList(index, elems.size()));
379: }
380:
381: public Name addAll(Name name) throws InvalidNameException {
382: if (null == name) {
383: throw new NullPointerException();
384: }
385: if (!(name instanceof CompositeName)) {
386: // jndi.0F=Must be a CompositeName
387: throw new InvalidNameException(Messages
388: .getString("jndi.0F")); //$NON-NLS-1$
389: }
390:
391: Enumeration<String> enumeration = name.getAll();
392: while (enumeration.hasMoreElements()) {
393: elems.add(enumeration.nextElement());
394: }
395: return this ;
396: }
397:
398: public Name addAll(int index, Name name)
399: throws InvalidNameException {
400: if (null == name) {
401: throw new NullPointerException();
402: }
403: if (!(name instanceof CompositeName)) {
404: // jndi.0F=Must be a CompositeName
405: throw new InvalidNameException(Messages
406: .getString("jndi.0F")); //$NON-NLS-1$
407: }
408:
409: if (index < 0 || index > elems.size()) {
410: throw new ArrayIndexOutOfBoundsException();
411: }
412: Enumeration<String> enumeration = name.getAll();
413: while (enumeration.hasMoreElements()) {
414: elems.add(index++, enumeration.nextElement());
415: }
416: return this ;
417: }
418:
419: public Name add(String element) throws InvalidNameException {
420: elems.add(element);
421: return this ;
422: }
423:
424: public Name add(int index, String element)
425: throws InvalidNameException {
426: if (index < 0 || index > elems.size()) {
427: throw new ArrayIndexOutOfBoundsException();
428: }
429: elems.add(index, element);
430: return this ;
431: }
432:
433: public Object remove(int index) throws InvalidNameException {
434: if (index < 0 || index >= elems.size()) {
435: throw new ArrayIndexOutOfBoundsException();
436: }
437: return elems.remove(index);
438: }
439:
440: public int size() {
441: return elems.size();
442: }
443:
444: public boolean isEmpty() {
445: return elems.isEmpty();
446: }
447:
448: public boolean startsWith(Name name) {
449: if (!(name instanceof CompositeName)) {
450: return false;
451: }
452:
453: // check size
454: if (name.size() > elems.size()) {
455: return false;
456: }
457:
458: // compare 1 by 1
459: Enumeration<String> enumeration = name.getAll();
460: String me, he;
461: for (int i = 0; enumeration.hasMoreElements(); i++) {
462: me = elems.get(i);
463: he = enumeration.nextElement();
464: if (!(null == me ? null == he : me.equals(he))) {
465: return false;
466: }
467: }
468: return true;
469: }
470:
471: public boolean endsWith(Name name) {
472: if (!(name instanceof CompositeName)) {
473: return false;
474: }
475:
476: // check size
477: if (name.size() > elems.size()) {
478: return false;
479: }
480:
481: // compare 1 by 1
482: Enumeration<String> enumeration = name.getAll();
483: String me, he;
484: for (int i = elems.size() - name.size(); enumeration
485: .hasMoreElements(); i++) {
486: me = elems.get(i);
487: he = enumeration.nextElement();
488: if (!(null == me ? null == he : me.equals(he))) {
489: return false;
490: }
491: }
492: return true;
493: }
494:
495: /**
496: * Compare this <code>Name</code> with the one supplied as a parameter.
497: * The elements of the names are compared in the same way as strings are
498: * compared to determine whether this <code>CompositeName</code> is less
499: * than, greater than or equal to the supplied object <code>o</code>.
500: *
501: * @param o
502: * the object to compare, cannot be null
503: * @return a negative number means this is less than the supplied object; a
504: * positive number means this is greater than the supplied object;
505: * zero means this CompositeName equals the object as specified in
506: * the description for the equals method of
507: * <code>CompositeName</code>.
508: * @throws ClassCastException
509: * when <code>o</code> is not a <code>CompositeName</code>.
510: */
511: public int compareTo(Object o) {
512: if (o instanceof CompositeName) {
513: CompositeName he = (CompositeName) o;
514: int r;
515: for (int i = 0; i < elems.size() && i < he.elems.size(); i++) {
516: r = (elems.get(i)).compareTo(he.elems.get(i));
517: if (r != 0) {
518: return r;
519: }
520: }
521: if (elems.size() == he.elems.size()) {
522: return 0;
523: } else if (elems.size() < he.elems.size()) {
524: return -1;
525: } else {
526: return 1;
527: }
528: }
529: throw new ClassCastException();
530: }
531:
532: /**
533: * Create a copy of this composite name, a complete (deep) copy of the
534: * object.
535: *
536: * @return a complete (deep) copy of the object.
537: */
538: @Override
539: public Object clone() {
540: return new CompositeName(elems);
541: }
542:
543: /**
544: * Returns the string representation of this <code>CompositeName</code>.
545: * This is generated by concatenating the elements together with the '/'
546: * char added as the separator between each of them. It may be necessary to
547: * add quotes and escape chars to preserve the meaning. The resulting string
548: * should produce an equivalent <code>CompositeName</code> when used to
549: * create a new instance.
550: *
551: * @return the string representation of this composite name.
552: */
553: @Override
554: public String toString() {
555: return formatName(elems);
556: }
557:
558: /**
559: * Check if this <code>CompositeName</code> is equal to the supplied
560: * object.
561: *
562: * @param o
563: * the <code>CompositeName</code> to compare - can be null but
564: * then returns false.
565: * @return true if they have the same number of elements all of which are
566: * equal. false if they are not equal.
567: */
568: @Override
569: public boolean equals(Object o) {
570: if (this == o) {
571: return true;
572: }
573:
574: // check type
575: if (!(o instanceof CompositeName)) {
576: return false;
577: }
578:
579: return this .elems.equals(((CompositeName) o).elems);
580: }
581:
582: /**
583: * Calculate the hashcode of this <code>CompositeName</code> by summing
584: * the hash codes of all of its elements.
585: *
586: * @return the hashcode of this object.
587: */
588: @Override
589: public int hashCode() {
590: int sum = 0;
591: for (int i = 0; i < elems.size(); i++) {
592: sum += elems.get(i).hashCode();
593: }
594: return sum;
595: }
596:
597: /**
598: * Writes a serialized representation of the CompositeName. It starts with
599: * an int which is the number of elements in the name, and is followed by a
600: * String for each element.
601: *
602: * @param oos
603: * @throws IOException
604: * if an error is encountered writing to the stream.
605: */
606: private void writeObject(ObjectOutputStream oos) throws IOException {
607: oos.defaultWriteObject();
608:
609: oos.writeInt(elems.size());
610: for (Object element : elems) {
611: oos.writeObject(element);
612: }
613: }
614:
615: /**
616: * Recreate a CompositeName from the data in the supplied stream.
617: *
618: * @param ois
619: * @throws IOException
620: * if an error is encountered reading from the stream.
621: * @throws ClassNotFoundException.
622: */
623: private void readObject(ObjectInputStream ois)
624: throws OptionalDataException, ClassNotFoundException,
625: IOException {
626: ois.defaultReadObject();
627:
628: int size = ois.readInt();
629: elems = new Vector<String>();
630: for (int i = 0; i < size; i++) {
631: elems.add((String) ois.readObject());
632: }
633: }
634:
635: }
|