0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: package javax.naming;
0019:
0020: import java.io.IOException;
0021: import java.io.ObjectInputStream;
0022: import java.io.ObjectOutputStream;
0023: import java.util.Enumeration;
0024: import java.util.Properties;
0025: import java.util.Vector;
0026:
0027: import org.apache.harmony.jndi.internal.nls.Messages;
0028:
0029: /**
0030: * A <code>CompoundName</code> is a series of string elements, and it
0031: * represents a name in a naming service within a single namespace. Typically
0032: * these names have a structure which is hierarchical.
0033: * <p>
0034: * A <code>CompoundName</code> has a sequence of zero or more elements
0035: * delimited by the char specified in the property "jndi.syntax.separator". This
0036: * property is required except when the direction of the name is "flat" (see
0037: * jndi.syntax.direction). The property "jndi.syntax.separator2" allows for the
0038: * specification of an additional separator. A separator string will be treated
0039: * as normal characters if it is preceded by the escape string or is within
0040: * quotes.
0041: * </p>
0042: * <p>
0043: * The property "jndi.syntax.direction" specifies the direction in which the
0044: * name is read. Permitted values are "right_to_left", "left_to_right" and
0045: * "flat". A flat name does not have a hierarchical structure. If this property
0046: * is not specified then the default is "flat". If this property is specified
0047: * with an invalid value then an <code>IllegalArgumentException</code> should
0048: * be raised.
0049: * </p>
0050: * <p>
0051: * Each element can be accessed using its position. The first element is at
0052: * position 0. The direction of the name is important. When direction is
0053: * "left_to_right" then the leftmost element is at position 0. Conversely when
0054: * the direction is "right_to_left" then the rightmost element is at position 0.
0055: * </p>
0056: * <p>
0057: * There are other properties which affect the syntax of a
0058: * <code>CompoundName</code>. The following properties are all optional:
0059: * <ul>
0060: * <li> jndi.syntax.escape - Escape sequence,The escape sequence is used to
0061: * escape a quote, separator or escape. When preceded itself by the escape
0062: * sequence it is treated as ordinary characters. When it is followed by chars
0063: * which are not quote or separator strings then it is treated as ordinary
0064: * characters</li>
0065: * <li> jndi.syntax.beginquote - Used as start of quoted string (Defaults to
0066: * endquote)</li>
0067: * <li> jndi.syntax.endquote - Used as end of quoted string (Defaults to
0068: * beginquote)</li>
0069: * <li> jndi.syntax.beginquote2 - Additionally used as start of quoted string
0070: * (Defaults to endquote2)</li>
0071: * <li> jndi.syntax.endquote2 - Additionally used as end of quoted string
0072: * (Defaults to beginquote2)</li>
0073: * </ul>
0074: * <p>
0075: * When a non-escaped quote appears at the start of an element it must be
0076: * matched at the end. That element can then be said to be quoted. When an
0077: * escape sequence appears within a quoted element then it is treated as normal
0078: * characters unless it precedes an occurrence of the quote in which case it is
0079: * assumed that the quoted element contains a quote which is escaped.
0080: * </p>
0081: * <p>
0082: * If the element does not start with a quote, then any quote strings within
0083: * that element are just normal characters.
0084: * </p>
0085: * <p>
0086: * <ul>
0087: * <li> jndi.syntax.ignorecase - If 'true' then ignore case when name elements
0088: * are compared. If false or not set then case is important.</li>
0089: * <li> jndi.syntax.trimblanks - If 'true' then ignore leading & trailing blanks
0090: * when name elements are compared. If false or not set then blanks are
0091: * important.</li>
0092: * </ul>
0093: * </p>
0094: * <p>
0095: * These 2 properties relate to names where the syntax includes
0096: * attribute/content pairs.
0097: * <ul>
0098: * <li>jndi.syntax.separator.ava</li>
0099: * <li>jndi.syntax.separator.typeval</li>
0100: * </ul>
0101: * For example the LDAP name, "CN=Mandy Jennings, O=Apache, C=UK". In this
0102: * example the pair separator jndi.syntax.separator.ava is ',', and the
0103: * character that separates pairs jndi.syntax.separator.typeval is '='. See
0104: * RFC1779 for LDAP naming conventions.
0105: * </p>
0106: * <p>
0107: * The jndi.syntax.separator.ava is not used when manipulating
0108: * <code>CompoundName</code>. The jndi.syntax.separator is still used to
0109: * separate elements.
0110: * </p>
0111: * <p>
0112: * The <code>CompoundName</code> needs to be aware of the
0113: * jndi.syntax.separator.typeval in case of the instance where a quoted string
0114: * is used to provide the content of a pair.
0115: * </p>
0116: * <p>
0117: * Consider the string "CN=$Mandy Jennings, O=Apache, C=UK" with
0118: * <ul>
0119: * <li> jndi.syntax.direction set to "right_to_left"</li>
0120: * <li> jndi.syntax.separator set to ","</li>
0121: * <li> jndi.syntax.separator.typeval set to "="</li>
0122: * </ul>
0123: * When no jndi.syntax.beginquote is set then this creates a valid
0124: * <code>CompoundName</code> with 3 elements.
0125: * </p>
0126: * <p>
0127: * If jndi.syntax.beginquote is then set to "$" the name becomes invalid as the
0128: * content part of the pair CN=$Mandy Jennings has a mismatched quote.
0129: * </p>
0130: * <p>
0131: * The string "CN=$Mandy Jennings$, O=Apache, C=UK" would be fine as the $
0132: * quotes round Mandy Jennings now balance.
0133: * </p>
0134: * <p>
0135: * A <code>CompoundName</code> may be empty. An empty
0136: * <code>CompoundName</code> has no elements. Elements may also be empty.
0137: * </p>
0138: *
0139: * <pre>
0140: * Some Examples:
0141: * ==============
0142: *
0143: * Consider the following compound name from the file system namespace:
0144: * "home/jenningm-abc/.profile"
0145: *
0146: * jndi.syntax.separator is set to '/' as in the UNIX filesystem.
0147: * This name has 3 elements:
0148: * home jenningm-abc and .profile
0149: * The direction should be left_to_right as in the UNIX filesystem
0150: * The element at position 0 would be home.
0151: *
0152: * Consider if jndi.syntax.separator had been set to '-' then this name
0153: * would have 2 elements:
0154: * home/jenningm and abc/.profile
0155: * If the direction was right_to_left then the element at position 0
0156: * would be abc/.profile.
0157: *
0158: * Consider the name "<ab<cd>ef>" where jndi.syntax.beginquote is <
0159: * and jndi.syntax.endquote is >. This will give rise to an
0160: * InvalidNameException because a close quote was encountered before
0161: * the end of an element. The same is also true for "<abcd>ef>".
0162: * If the name was "ab<cd>ef" then this would be valid and there would
0163: * be one element ab<cd>ef.
0164: * However if the name was "<abcdef>" there would be one element abcdef.
0165: *
0166: * An empty
0167: * <code>
0168: * CompoundName
0169: * </code>
0170: * is the name "" and has no elements.
0171: *
0172: * When jndi.syntax.beginquote is set to " and beginquote2 is set to '
0173: * the behaviour is similar to CompositeName -
0174: * The name "\"abcd" gives an InvalidNameException as there is no closing quote.
0175: * The name "'\"abcd'" gives one element of value "abcd.
0176: * The name "\\abcd" gives one element of value \abcd.
0177: *
0178: * Assuming:
0179: * jndi.syntax.separator is "/"
0180: * jndi.syntax.direction is "left_to_right"
0181: * then
0182: * "" is empty. It has no elements.
0183: * "/" has one empty element.
0184: * "//" has 2 empty elements.
0185: * "/a/" has 3 elements the middle one is set to a.
0186: * "///" has 3 empty elements.
0187: * "//a/" has 4 elements, the last but one is set to a.
0188: *
0189: *
0190: * Assuming the only properties set are:
0191: * jndi.syntax.separator is "/"
0192: * jndi.syntax.direction is "left_to_right"
0193: * then the String
0194: * "\"" has one element with the value "
0195: * "\\\"" has one element with the value \"
0196: * "\\\"'" has one element with the value \"'
0197: *
0198: * Assuming the only properties set are:
0199: * jndi.syntax.separator is "/"
0200: * jndi.syntax.direction is "left_to_right"
0201: * jndi.syntax.beginquote is "\""
0202: *
0203: * then the String
0204: * "\"" is invalid because of no closing quote
0205: * "\\\"" has one element with the value \"
0206: * "\\\"'" has one element with the value \"'
0207: *
0208: * Assuming the only properties set are:
0209: * jndi.syntax.separator is "/"
0210: * jndi.syntax.direction is "left_to_right"
0211: * jndi.syntax.beginquote is "\""
0212: * jndi.syntax.beginquote2 is "\'"
0213: * then the String
0214: * "\"" is invalid because of no closing quote
0215: * "\\\"" has one element with the value \"
0216: * "\\\"'" has one element with the value \"'
0217: * "'\\" is invalid because of no closing quote
0218: * </pre>
0219: */
0220: public class CompoundName implements Name {
0221:
0222: /*
0223: * Note: For serialization purposes, the specified serialVersionUID must be
0224: * used. This class does not have serializable fields specified. Instead the
0225: * readObject and writeObject methods are overridden.
0226: */
0227: private static final long serialVersionUID = 3513100557083972036L;
0228:
0229: // const for property key
0230: private static final String SEPARATOR = "jndi.syntax.separator"; //$NON-NLS-1$
0231:
0232: private static final String SEPARATOR_AVA = "jndi.syntax.separator.ava"; //$NON-NLS-1$
0233:
0234: private static final String SEPARATOR_TYPEVAL = "jndi.syntax.separator.typeval"; //$NON-NLS-1$
0235:
0236: private static final String ESCAPE = "jndi.syntax.escape"; //$NON-NLS-1$
0237:
0238: private static final String BEGIN_QUOTE = "jndi.syntax.beginquote"; //$NON-NLS-1$
0239:
0240: private static final String END_QUOTE = "jndi.syntax.endquote"; //$NON-NLS-1$
0241:
0242: private static final String BEGIN_QUOTE2 = "jndi.syntax.beginquote2"; //$NON-NLS-1$
0243:
0244: private static final String END_QUOTE2 = "jndi.syntax.endquote2"; //$NON-NLS-1$
0245:
0246: private static final String IGNORE_CASE = "jndi.syntax.ignorecase"; //$NON-NLS-1$
0247:
0248: private static final String TRIM_BLANKS = "jndi.syntax.trimblanks"; //$NON-NLS-1$
0249:
0250: private static final String DIRECTION = "jndi.syntax.direction"; //$NON-NLS-1$
0251:
0252: private static final String SEPARATOR2 = "jndi.syntax.separator2"; //$NON-NLS-1$
0253:
0254: // const for direction
0255: private static final String LEFT_TO_RIGHT = "left_to_right"; //$NON-NLS-1$
0256:
0257: private static final String RIGHT_TO_LEFT = "right_to_left"; //$NON-NLS-1$
0258:
0259: private static final String FLAT = "flat"; //$NON-NLS-1$
0260:
0261: // alphabets consts
0262: private static final String NULL_STRING = ""; //$NON-NLS-1$
0263:
0264: // states consts
0265: private static final int NORMAL_STATUS = 0;
0266:
0267: private static final int QUOTE1_STATUS = 1;
0268:
0269: private static final int QUOTE2_STATUS = 2;
0270:
0271: private static final int INIT_STATUS = 3;
0272:
0273: private static final int QUOTEEND_STATUS = 4;
0274:
0275: // properties variables
0276: private transient String separatorString;
0277:
0278: private transient String separatorString2;
0279:
0280: private transient String escapeString;
0281:
0282: private transient String endQuoteString;
0283:
0284: private transient String endQuoteString2;
0285:
0286: private transient String beginQuoteString;
0287:
0288: private transient String beginQuoteString2;
0289:
0290: private transient String sepAvaString;
0291:
0292: private transient String sepTypeValString;
0293:
0294: private transient String direction;
0295:
0296: private transient boolean trimBlanks;
0297:
0298: private transient boolean ignoreCase;
0299:
0300: private transient boolean flat;
0301:
0302: // elements of compound name
0303: private transient Vector<String> elem;
0304:
0305: // property setting
0306: protected transient Properties mySyntax;
0307:
0308: /*
0309: * The specification calls for a protected variable called 'impl' which is
0310: * of a non-API type. I believe this is an error in the spec, but to be
0311: * complaint we have implemented this as a useless class (below).
0312: */
0313: protected transient javax.naming.NameImpl impl = new NameImpl();
0314:
0315: /**
0316: * Constructs a <code>CompoundName</code> with supplied
0317: * <code>Enumeration</code> and <code>Properties</code>
0318: *
0319: * @param elements
0320: * an enumeration of name elements, cannot be null
0321: * @param props
0322: * the properties, cannot be null but may be empty. If empty, the
0323: * direction defaults to flat and no other properties are
0324: * required.
0325: */
0326: protected CompoundName(Enumeration<String> elements,
0327: Properties props) {
0328: if (null == props || null == elements) {
0329: throw new NullPointerException();
0330: }
0331: init(props);
0332: this .elem = new Vector<String>();
0333: while (elements.hasMoreElements()) {
0334: this .elem.add(elements.nextElement());
0335: }
0336: }
0337:
0338: /**
0339: * Constructs a <code>CompoundName</code> with supplied
0340: * <code>String</code> and <code>Properties</code>, taking the supplied
0341: * <code>s</code> and breaking it down into its elements.
0342: *
0343: * @param s
0344: * a string containing the full compound name
0345: * @param props
0346: * the properties, cannot be null but may be empty for a flat
0347: * name
0348: * @throws InvalidNameException
0349: * thrown if the supplied <code>String s</code> is invalid
0350: * @throws NullPointerException
0351: * thrown if the supplied <code>String s</code> is null
0352: */
0353: public CompoundName(String s, Properties props)
0354: throws InvalidNameException {
0355: if (null == s || null == props) {
0356: throw new NullPointerException();
0357: }
0358: init(props);
0359: parseName(s);
0360: }
0361:
0362: /**
0363: * init instance variables
0364: */
0365: private void init(Properties props) {
0366: trimBlanks = false;
0367: ignoreCase = false;
0368: this .mySyntax = props;
0369: String property;
0370:
0371: // read property settings
0372: // direction's default value is FLAT
0373: direction = null == (property = props.getProperty(DIRECTION)) ? FLAT
0374: : property;
0375: // if direction value must equals to one of FLAT, LEFT_TO_RIGHT and
0376: // RIGHT_TO_LEFT, exception threw
0377: if (!LEFT_TO_RIGHT.equals(direction)
0378: && !RIGHT_TO_LEFT.equals(direction)
0379: && !FLAT.equals(direction)) {
0380: // jndi.04=Illegal direction property value, which must be one of
0381: // right_to_left, left_to_right or flat
0382: throw new IllegalArgumentException(Messages
0383: .getString("jndi.04")); //$NON-NLS-1$
0384: }
0385: flat = FLAT.equals(direction);
0386:
0387: separatorString = flat ? NULL_STRING : props
0388: .getProperty(SEPARATOR);
0389: // if direction is not FLAT, separator must be set
0390: if (null == separatorString && !flat) {
0391: // jndi.05=jndi.syntax.separator property must be set when
0392: // jndi.syntax.direction is not flat
0393: throw new IllegalArgumentException(Messages
0394: .getString("jndi.05")); //$NON-NLS-1$
0395: }
0396: separatorString2 = (flat || null == (property = props
0397: .getProperty(SEPARATOR2))) ? NULL_STRING : property;
0398:
0399: // ignorecase default value is false
0400: ignoreCase = null == (property = props.getProperty(IGNORE_CASE)) ? false
0401: : Boolean.valueOf(property).booleanValue();
0402: // trimblanks default value is false
0403: trimBlanks = null == (property = props.getProperty(TRIM_BLANKS)) ? false
0404: : Boolean.valueOf(property).booleanValue();
0405: escapeString = null == (property = props.getProperty(ESCAPE)) ? NULL_STRING
0406: : property;
0407: beginQuoteString = null == (property = props
0408: .getProperty(BEGIN_QUOTE)) ? NULL_STRING : property;
0409: beginQuoteString2 = null == (property = props
0410: .getProperty(BEGIN_QUOTE2)) ? NULL_STRING : property;
0411: // end quote string default value is begin quote string
0412: endQuoteString = null == (property = props
0413: .getProperty(END_QUOTE)) ? beginQuoteString : property;
0414: // begin quote string default value is end quote string
0415: if (NULL_STRING.equals(beginQuoteString)) {
0416: beginQuoteString = endQuoteString;
0417: }
0418: // end quote string2 default value is begin quote string2
0419: endQuoteString2 = null == (property = props
0420: .getProperty(END_QUOTE2)) ? beginQuoteString2
0421: : property;
0422: // begin quote string2 default value is end quote string2
0423: if (NULL_STRING.equals(beginQuoteString2)) {
0424: beginQuoteString2 = endQuoteString2;
0425: }
0426:
0427: sepTypeValString = null == (property = props
0428: .getProperty(SEPARATOR_TYPEVAL)) ? NULL_STRING
0429: : property;
0430: sepAvaString = null == (property = props
0431: .getProperty(SEPARATOR_AVA)) ? NULL_STRING : property;
0432: }
0433:
0434: /*
0435: * parse name from string to elements
0436: */
0437: private void parseName(String s) throws InvalidNameException {
0438: this .elem = new Vector<String>();
0439: if ("".equals(s)) { //$NON-NLS-1$
0440: // if empty string, return empty vector
0441: return;
0442: }
0443:
0444: // init variables
0445: int status = INIT_STATUS;
0446: StringBuilder element = new StringBuilder();
0447: int pos = 0;
0448: int length = s.length();
0449: boolean hasNotNullElement = false;
0450: boolean includeQuote = false;
0451:
0452: // scan name
0453: while (pos < length) {
0454: if (startsWithFromPos(s, pos, endQuoteString)
0455: && status == QUOTE1_STATUS) {
0456: status = QUOTEEND_STATUS;
0457: pos += addBuffer(element, endQuoteString, includeQuote);
0458: } else if (startsWithFromPos(s, pos, endQuoteString2)
0459: && status == QUOTE2_STATUS) {
0460: status = QUOTEEND_STATUS;
0461: pos += addBuffer(element, endQuoteString2, includeQuote);
0462: } else if (startsWithFromPos(s, pos, beginQuoteString)
0463: && status == INIT_STATUS) {
0464: hasNotNullElement = true;
0465: status = QUOTE1_STATUS;
0466: pos += addBuffer(element, beginQuoteString,
0467: includeQuote);
0468: } else if (startsWithFromPos(s, pos, beginQuoteString2)
0469: && status == INIT_STATUS) {
0470: hasNotNullElement = true;
0471: status = QUOTE2_STATUS;
0472: pos += addBuffer(element, beginQuoteString2,
0473: includeQuote);
0474: } else if (startsWithFromPos(s, pos, separatorString)
0475: && (!flat)
0476: && (status == INIT_STATUS
0477: || status == QUOTEEND_STATUS || status == NORMAL_STATUS)) {
0478: hasNotNullElement = hasNotNullElement
0479: || element.length() > 0;
0480: addElement(element);
0481: status = INIT_STATUS;
0482: pos += separatorString.length();
0483: includeQuote = false;
0484: } else if (startsWithFromPos(s, pos, separatorString2)
0485: && (!flat)
0486: && (status == INIT_STATUS
0487: || status == QUOTEEND_STATUS || status == NORMAL_STATUS)) {
0488: hasNotNullElement = hasNotNullElement
0489: || element.length() > 0;
0490: addElement(element);
0491: status = INIT_STATUS;
0492: pos += separatorString2.length();
0493: includeQuote = false;
0494: } else if (startsWithFromPos(s, pos, escapeString)) {
0495: pos += escapeString.length();
0496: if (pos == s.length()) {
0497: // if this escape char is last character, throw exception
0498: // jndi.06=The {0} cannot be at end of the component
0499: throw new InvalidNameException(Messages.getString(
0500: "jndi.06", escapeString)); //$NON-NLS-1$
0501: }
0502: // if one escape char followed by a special char, append the
0503: // special char to current element
0504: String str = extractEscapedString(s, pos, status);
0505: if (null == str) {
0506: pos -= escapeString.length();
0507: element.append(s.charAt(pos++));
0508: } else {
0509: pos += str.length();
0510: element.append(str);
0511: }
0512:
0513: } else if (startsWithFromPos(s, pos, sepTypeValString)
0514: && (status == INIT_STATUS || status == NORMAL_STATUS)) {
0515: includeQuote = true;
0516: pos += addBuffer(element, sepTypeValString, true);
0517: status = INIT_STATUS;
0518: } else if (startsWithFromPos(s, pos, sepAvaString)
0519: && (status == INIT_STATUS || status == NORMAL_STATUS)) {
0520: includeQuote = true;
0521: pos += addBuffer(element, sepAvaString, true);
0522: status = INIT_STATUS;
0523: } else if (status == QUOTEEND_STATUS) {
0524: // jndi.07={0}: close quote must appears at end of component in
0525: // quoted string
0526: throw new InvalidNameException(Messages.getString(
0527: "jndi.07", s)); //$NON-NLS-1$
0528: } else {
0529: status = status == INIT_STATUS ? NORMAL_STATUS : status;
0530: element.append(s.charAt(pos++));
0531: }
0532: }
0533: if (QUOTE1_STATUS != status && QUOTE2_STATUS != status) {
0534: hasNotNullElement = hasNotNullElement
0535: || element.length() > 0;
0536: addElement(element);
0537: } else {
0538: // jndi.08={0}: close quote is required for quoted string
0539: throw new InvalidNameException(Messages.getString(
0540: "jndi.08", s)); //$NON-NLS-1$
0541: }
0542: if (!hasNotNullElement) {
0543: elem.remove(elem.size() - 1);
0544: }
0545: }
0546:
0547: /*
0548: * add des parameter to StringBuilder if include is true
0549: */
0550: private int addBuffer(StringBuilder buffer, String des,
0551: boolean include) {
0552: if (include) {
0553: buffer.append(des);
0554: }
0555: return des.length();
0556: }
0557:
0558: /*
0559: * add current content of supplied string buffer as one element of this
0560: * CompoundName and reset the string buffer to empty
0561: */
0562: private void addElement(StringBuilder element) {
0563: if (LEFT_TO_RIGHT == direction) {
0564: elem.add(element.toString());
0565: } else {
0566: elem.add(0, element.toString());
0567: }
0568: element.setLength(0);
0569: }
0570:
0571: /*
0572: * find string to be escaped, if cannot find special string(which means,
0573: * quote, separator and escape), return null
0574: */
0575: private String extractEscapedString(String s, int pos, int status) {
0576: String result = null;
0577: if (status == QUOTE1_STATUS
0578: && startsWithFromPos(s, pos, endQuoteString)) {
0579: result = endQuoteString;
0580: } else if (status == QUOTE2_STATUS
0581: && startsWithFromPos(s, pos, endQuoteString2)) {
0582: result = endQuoteString2;
0583: } else if (status != QUOTE1_STATUS && status != QUOTE2_STATUS) {
0584: if (startsWithFromPos(s, pos, beginQuoteString)) {
0585: result = beginQuoteString;
0586: } else if (startsWithFromPos(s, pos, beginQuoteString2)) {
0587: result = beginQuoteString2;
0588: } else if (startsWithFromPos(s, pos, endQuoteString)) {
0589: result = endQuoteString;
0590: } else if (startsWithFromPos(s, pos, endQuoteString2)) {
0591: result = endQuoteString2;
0592: } else if (startsWithFromPos(s, pos, separatorString)) {
0593: result = separatorString;
0594: } else if (startsWithFromPos(s, pos, separatorString2)) {
0595: result = separatorString2;
0596: } else if (startsWithFromPos(s, pos, escapeString)) {
0597: result = escapeString;
0598: }
0599: }
0600: return result;
0601: }
0602:
0603: /*
0604: * justify if string src start with des from position pos
0605: */
0606: private boolean startsWithFromPos(String src, int pos, String des) {
0607: if (null == src || null == des || NULL_STRING.equals(des)
0608: || src.length() - pos < des.length()) {
0609: return false;
0610: }
0611: int length = des.length();
0612: int i = -1;
0613: while (++i < length && src.charAt(pos + i) == des.charAt(i)) {
0614: // empty body
0615: }
0616: return i == length;
0617: }
0618:
0619: public Enumeration<String> getAll() {
0620: return this .elem.elements();
0621: }
0622:
0623: public String get(int index) {
0624: validateIndex(index, false);
0625: return elem.elementAt(index);
0626: }
0627:
0628: /*
0629: * validate the index, if isInclude is true, index which equals to
0630: * this.size() is considered as valid, otherwise invalid
0631: */
0632: private void validateIndex(int index, boolean isInclude) {
0633: if (0 > index || index > elem.size()
0634: || (!isInclude && index == elem.size())) {
0635: throw new ArrayIndexOutOfBoundsException();
0636: }
0637: }
0638:
0639: public Name getPrefix(int index) {
0640: validateIndex(index, true);
0641: return new CompoundName(new Vector<String>(elem.subList(0,
0642: index)).elements(), mySyntax);
0643: }
0644:
0645: public Name getSuffix(int index) {
0646: validateIndex(index, false);
0647: return new CompoundName(new Vector<String>(elem.subList(index,
0648: elem.size())).elements(), mySyntax);
0649: }
0650:
0651: public Name addAll(Name name) throws InvalidNameException {
0652: return addAll(elem.size(), name);
0653: }
0654:
0655: public Name addAll(int index, Name name)
0656: throws InvalidNameException {
0657: if (name == null) {
0658: // jndi.00=name must not be null
0659: throw new NullPointerException(Messages
0660: .getString("jndi.00")); //$NON-NLS-1$
0661: }
0662: if (!(name instanceof CompoundName)) {
0663: // jndi.09={0} is not a compound name.
0664: throw new InvalidNameException(Messages.getString(
0665: "jndi.09", name.toString())); //$NON-NLS-1$
0666: }
0667: if (FLAT.equals(direction) && (this .size() + name.size() > 1)) {
0668: // jndi.0A=A flat name can only have a single component
0669: throw new InvalidNameException(Messages
0670: .getString("jndi.0A")); //$NON-NLS-1$
0671: }
0672: validateIndex(index, true);
0673: Enumeration<String> enumeration = name.getAll();
0674: while (enumeration.hasMoreElements()) {
0675: elem.add(index++, enumeration.nextElement());
0676: }
0677: return this ;
0678: }
0679:
0680: public Name add(String element) throws InvalidNameException {
0681: if (element == null) {
0682: // jndi.8C=component must not be null
0683: throw new IllegalArgumentException(Messages
0684: .getString("jndi.8C")); //$NON-NLS-1$
0685: }
0686: if (FLAT.equals(direction) && (size() > 0)) {
0687: // jndi.0A=A flat name can only have a single component
0688: throw new InvalidNameException(Messages
0689: .getString("jndi.0A")); //$NON-NLS-1$
0690: }
0691: elem.add(element);
0692: return this ;
0693: }
0694:
0695: /**
0696: * Insert an element within this CompoundName at the specified index.
0697: *
0698: * @return this <code>CompoundName</code>.
0699: * @param element
0700: * the String to insert
0701: * @param index
0702: * the index of the element to insert - must be greater than or
0703: * equal to 0 and less than size().
0704: * @throws ArrayIndexOutOfBoundsException
0705: * thrown when the index is invalid.
0706: * @throws InvalidNameException
0707: * thrown if the insertion of the element results in this
0708: * <code>CompoundName</code> becoming invalid.
0709: */
0710: public Name add(int index, String element)
0711: throws InvalidNameException {
0712: if (element == null) {
0713: // jndi.8C=component must not be null
0714: throw new IllegalArgumentException(Messages
0715: .getString("jndi.8C")); //$NON-NLS-1$
0716: }
0717: if (FLAT.equals(direction) && (size() > 0)) {
0718: // jndi.0A=A flat name can only have a single component
0719: throw new InvalidNameException(Messages
0720: .getString("jndi.0A")); //$NON-NLS-1$
0721: }
0722: validateIndex(index, true);
0723: elem.add(index, element);
0724: return this ;
0725: }
0726:
0727: /**
0728: * Delete an element from this <code>CompoundName</code>.
0729: *
0730: * @return the deleted element
0731: * @param index
0732: * the index of the element to delete - must be greater than or
0733: * equal to 0 and less than size().
0734: * @throws ArrayIndexOutOfBoundsException
0735: * thrown when the index is invalid.
0736: * @throws InvalidNameException
0737: * thrown if the deletion of the element results in this
0738: * <code>CompoundName</code> becoming invalid.
0739: */
0740: public Object remove(int index) throws InvalidNameException {
0741: validateIndex(index, false);
0742: return elem.remove(index);
0743: }
0744:
0745: @Override
0746: public Object clone() {
0747: return new CompoundName(getAll(), mySyntax);
0748: }
0749:
0750: public int size() {
0751: return elem.size();
0752: }
0753:
0754: public boolean isEmpty() {
0755: return elem.isEmpty();
0756: }
0757:
0758: public boolean startsWith(Name name) {
0759: if (!(name instanceof CompoundName)) {
0760: return false;
0761: }
0762: return equals(name, 0, name.size());
0763: }
0764:
0765: public boolean endsWith(Name name) {
0766: if (!(name instanceof CompoundName)) {
0767: return false;
0768: }
0769: return equals(name, this .size() - name.size(), name.size());
0770: }
0771:
0772: /**
0773: * preprocess string according to trimblank and ignorecase properties
0774: */
0775: private String preProcess(String string, boolean caseInsensitive,
0776: boolean removeBlanks) {
0777: String result = string;
0778: if (null != string && !"".equals(string)) { //$NON-NLS-1$
0779: result = caseInsensitive ? result.toLowerCase() : result;
0780: result = removeBlanks ? result.trim() : result;
0781: }
0782: return result;
0783: }
0784:
0785: /**
0786: * Writes a serialized representation of the CompoundName. It starts with
0787: * the properties, followed by an int which is the number of elements in the
0788: * name, and is followed by a String for each element.
0789: *
0790: * @throws java.io.IOException
0791: * if an error is encountered writing to the stream.
0792: */
0793: private void writeObject(ObjectOutputStream oos) throws IOException {
0794: oos.defaultWriteObject();
0795: oos.writeObject(mySyntax);
0796: oos.writeInt(elem.size());
0797: for (int i = 0; i < elem.size(); i++) {
0798: String element = elem.elementAt(i);
0799: oos.writeObject(element);
0800: }
0801: }
0802:
0803: /**
0804: * Recreate a CompoundName from the data in the supplied stream.
0805: * Additionally there are 2 protected fields which are not serializable. One
0806: * of them is of a type which is a private class and cannot therefore be
0807: * specified or implemented and so will be excluded from our deliverable.
0808: * The one protected field which we can spec and implement is as follows:
0809: * protected Properties mySyntax - The properties associated with a
0810: * CompoundName.
0811: *
0812: * @throws java.io.IOException
0813: * if an error is encountered reading from the stream.
0814: * @throws ClassNotFoundException.
0815: */
0816: private void readObject(ObjectInputStream ois)
0817: throws ClassNotFoundException, IOException {
0818: ois.defaultReadObject();
0819: init(((Properties) ois.readObject()));
0820: int size = ois.readInt();
0821: elem = new Vector<String>();
0822: for (int i = 0; i < size; i++) {
0823: elem.add((String) ois.readObject());
0824: }
0825: }
0826:
0827: /**
0828: * Compare this <code>CompoundName</code> with the one supplied as a
0829: * param.
0830: * <p>
0831: * See the definition of the <code>equals()</code> method to see how the
0832: * direction, ignorecase and trimblanks properties affect the comparison of
0833: * a <code>CompoundName</code>. Other than that the comparison is the
0834: * same as that for a <code>CompositeName</code>.
0835: * </p>
0836: *
0837: * @return a negative number means this is less than the supplied Object
0838: * <code>o</code>. a positive number means this is greater than
0839: * the supplied Object <code>o</code>. zero means the two objects
0840: * are equal.
0841: * @param o
0842: * the object to compare - cannot be null.
0843: * @throws ClassCastException
0844: * when <code>o</code> is not a compatible class that can be
0845: * compared or if the object to compare <code>o</code> is
0846: * null.
0847: */
0848: public int compareTo(Object o) {
0849: if (!(o instanceof CompoundName)) {
0850: throw new ClassCastException();
0851: }
0852: int result = -1;
0853: CompoundName otherName = (CompoundName) o;
0854: Enumeration<String> otherEnum = otherName.getAll();
0855: String this Element;
0856: String otherElement;
0857: int i;
0858: for (i = 0; i < size() && otherEnum.hasMoreElements(); i++) {
0859: this Element = preProcess(elem.get(i), ignoreCase,
0860: trimBlanks);
0861: otherElement = preProcess(otherEnum.nextElement(),
0862: ignoreCase, trimBlanks);
0863: result = (null == this Element ? (null == otherElement ? 0
0864: : -1) : this Element.compareTo(otherElement));
0865: if (0 != result) {
0866: return result;
0867: }
0868: }
0869: if (i < size()) {
0870: result = 1;
0871: } else if (otherEnum.hasMoreElements()) {
0872: result = -1;
0873: }
0874: return result;
0875: }
0876:
0877: /**
0878: * Calculate the hashcode of this <code>CompoundName</code> by summing the
0879: * hashcodes of all of its elements.
0880: * <p>
0881: * If jndi.syntax.trimblanks is set to true then remove any leading and
0882: * trailing blanks from the elements before calculating the hashcode.
0883: * </p>
0884: * <p>
0885: * If jndi.syntax.ignorecase is set to true then use the lowercase version
0886: * of the element to calculate its hashcode.
0887: * </p>
0888: *
0889: * @return the hashcode of this object.
0890: */
0891: @Override
0892: public int hashCode() {
0893: int result = 0;
0894: Enumeration<String> enumeration = elem.elements();
0895: while (enumeration.hasMoreElements()) {
0896: result += preProcess(enumeration.nextElement(), ignoreCase,
0897: trimBlanks).hashCode();
0898: }
0899: return result;
0900: }
0901:
0902: /**
0903: * Gets the string representation of this <code>CompoundName</code>.
0904: * <p>
0905: * This is generated by concatenating the elements together with the
0906: * separator string added as the separator between each of them. It may be
0907: * necessary to add quotes and escape string to preserve the meaning. The
0908: * resulting string should produce an equivalent <code>CompoundName</code>
0909: * when used to create a new instance.
0910: * </p>
0911: *
0912: * @return the string representation of this <code>CompoundName</code>.
0913: */
0914: @Override
0915: public String toString() {
0916: StringBuilder sb = new StringBuilder();
0917: String begin = NULL_STRING.equals(beginQuoteString) ? beginQuoteString2
0918: : beginQuoteString;
0919: String end = NULL_STRING.equals(endQuoteString) ? endQuoteString2
0920: : endQuoteString;
0921: String separator = NULL_STRING.equals(separatorString) ? separatorString2
0922: : separatorString;
0923: if (RIGHT_TO_LEFT.equals(direction)) {
0924: for (int i = elem.size() - 1; i >= 0; i--) {
0925: addElement(sb, i, separator, begin, end);
0926: }
0927: } else {
0928: for (int i = 0; i < elem.size(); i++) {
0929: addElement(sb, i, separator, begin, end);
0930: }
0931: }
0932: if (size() * separator.length() < sb.length()) {
0933: // if the name contains non-empty element, delete the last separator
0934: // char, which is abundant
0935: sb.setLength(sb.length() - separator.length());
0936: }
0937: return sb.toString();
0938: }
0939:
0940: private void addElement(StringBuilder sb, int index,
0941: String separator, String begin, String end) {
0942: String elemString = elem.get(index);
0943: if (0 == elemString.length()) {
0944: // if empty element, append a separator and continue
0945: sb.append(separator);
0946: return;
0947: }
0948: int pos = sb.length();
0949: sb.append(elemString);
0950: if (!NULL_STRING.equals(begin) && !NULL_STRING.equals(end)
0951: && !NULL_STRING.equals(separator)
0952: && (0 <= elemString.indexOf(separator))) {
0953: // if contains separator string, quoted it
0954: sb.insert(pos, begin);
0955: pos += begin.length();
0956: // if quoted, then every endquote char must be escaped
0957: for (int i = 0, j = 0; 0 <= (j = elemString.indexOf(end, i)); i = j
0958: + end.length()) {
0959: sb.insert(pos + j, escapeString);
0960: pos += escapeString.length();
0961: }
0962: sb.append(end);
0963: } else {
0964: if (startsWithFromPos(elemString, 0, beginQuoteString)
0965: || startsWithFromPos(elemString, 0,
0966: beginQuoteString2)) {
0967: // if not quoted and start with begin quote string, escape it
0968: sb.insert(pos, escapeString);
0969: pos += escapeString.length();
0970: }
0971: // if not quoted, escape all separator string and all escape string
0972: for (int i = 0; i < elemString.length();) {
0973: if (startsWithFromPos(elemString, i, separatorString)) {
0974: sb.insert(pos + i, escapeString);
0975: pos += escapeString.length();
0976: i += separatorString.length();
0977: } else if (startsWithFromPos(elemString, i,
0978: separatorString2)) {
0979: sb.insert(pos + i, escapeString);
0980: pos += escapeString.length();
0981: i += separatorString2.length();
0982: } else if (startsWithFromPos(elemString, i,
0983: escapeString)) {
0984: sb.insert(pos + i, escapeString);
0985: pos += escapeString.length();
0986: i += escapeString.length();
0987: } else {
0988: i++;
0989: }
0990: }
0991: }
0992: sb.append(separator);
0993: }
0994:
0995: /**
0996: * Check if the supplied object <code>o</code> is equal to this
0997: * <code>CompoundName</code>.
0998: * <p>
0999: * The supplied <code>Object o</code> may be null but that will cause
1000: * false to be returned.
1001: * </p>
1002: * <p>
1003: * The supplied <code>Object o</code> may be something other than a
1004: * <code>CompoundName</code> but that will cause false to be returned.
1005: * </p>
1006: * <p>
1007: * To be equal the supplied <code>CompoundName</code> must have the same
1008: * number of elements and each element must match the corresponding element
1009: * of this <code>CompoundName</code>. The properties
1010: * jndi.syntax.ignorecase and jndi.syntax.trimblanks need to be considered
1011: * if they have been set.
1012: * </p>
1013: * <p>
1014: * The properties associated with the <code>CompoundName</code> must be
1015: * taken into account but do not have to match. For example
1016: * "home/jenningm-abc/.profile" with a direction of left to right is equal
1017: * to ".profile/jenningm-abc/home" with a direction of right to left.
1018: * </p>
1019: *
1020: * @param o
1021: * the object to be compared
1022: * @return true if supplied object <code>o</code> is equals to this
1023: * <code>CompoundName</code>, false otherwise
1024: */
1025: @Override
1026: public boolean equals(Object o) {
1027: if (!(o instanceof CompoundName)) {
1028: return false;
1029: }
1030:
1031: // compare size
1032: CompoundName otherName = (CompoundName) o;
1033: final int size = otherName.size();
1034: if (size != this .size()) {
1035: return false;
1036: }
1037:
1038: // compare every element
1039: return equals(otherName, 0, size);
1040: }
1041:
1042: /**
1043: * compare this name to the supplied <code>name</code> from position
1044: * <code>start</code> to position <code>start</code>+
1045: * <code>length</code>-1
1046: */
1047: private boolean equals(Name name, int start, int length) {
1048: if (length > this .size()) {
1049: return false;
1050: }
1051: CompoundName otherName = (CompoundName) name;
1052: Enumeration<String> otherEnum = otherName.getAll();
1053: String this Element;
1054: String otherElement;
1055: for (int i = 0; i < length; i++) {
1056: this Element = preProcess(elem.get(i + start), ignoreCase,
1057: trimBlanks);
1058: otherElement = preProcess(otherEnum.nextElement(),
1059: ignoreCase, trimBlanks);
1060: if (!(null == this Element ? null == otherElement
1061: : this Element.equals(otherElement))) {
1062: return false;
1063: }
1064: }
1065: return true;
1066: }
1067:
1068: }
|