Path: blob/master/src/java.naming/share/classes/com/sun/jndi/ldap/LdapSchemaParser.java
41161 views
/*1* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.jndi.ldap;2627import javax.naming.*;28import javax.naming.directory.*;29import java.util.Vector;3031/**32* Netscape's 3.1 servers have some schema bugs:33* - It puts quotes around OIDs (such as those for SUP, SYNTAX).34* - When you try to write out the MUST/MAY list (such as "MUST cn"),35* it wants ("MUST (cn)") instead36*/3738final class LdapSchemaParser {3940// do debugging41private static final boolean debug = false;424344// names of attribute IDs in the LDAP schema entry45static final String OBJECTCLASSDESC_ATTR_ID = "objectClasses";46static final String ATTRIBUTEDESC_ATTR_ID = "attributeTypes";47static final String SYNTAXDESC_ATTR_ID = "ldapSyntaxes";48static final String MATCHRULEDESC_ATTR_ID = "matchingRules";4950// information for creating internal nodes in JNDI schema tree51static final String OBJECTCLASS_DEFINITION_NAME =52"ClassDefinition";53private static final String[] CLASS_DEF_ATTRS = {54"objectclass", "ClassDefinition"};55static final String ATTRIBUTE_DEFINITION_NAME =56"AttributeDefinition";57private static final String[] ATTR_DEF_ATTRS = {58"objectclass", "AttributeDefinition" };59static final String SYNTAX_DEFINITION_NAME =60"SyntaxDefinition";61private static final String[] SYNTAX_DEF_ATTRS = {62"objectclass", "SyntaxDefinition" };63static final String MATCHRULE_DEFINITION_NAME =64"MatchingRule";65private static final String[] MATCHRULE_DEF_ATTRS = {66"objectclass", "MatchingRule" };6768// special tokens used in LDAP schema descriptions69private static final char SINGLE_QUOTE = '\'';70private static final char WHSP = ' ';71private static final char OID_LIST_BEGIN = '(';72private static final char OID_LIST_END = ')';73private static final char OID_SEPARATOR = '$';7475// common IDs76private static final String NUMERICOID_ID = "NUMERICOID";77private static final String NAME_ID = "NAME";78private static final String DESC_ID = "DESC";79private static final String OBSOLETE_ID = "OBSOLETE";80private static final String SUP_ID = "SUP";81private static final String PRIVATE_ID = "X-";8283// Object Class specific IDs84private static final String ABSTRACT_ID = "ABSTRACT";85private static final String STRUCTURAL_ID = "STRUCTURAL";86private static final String AUXILIARY_ID = "AUXILIARY";87private static final String MUST_ID = "MUST";88private static final String MAY_ID = "MAY";8990// Attribute Type specific IDs91private static final String EQUALITY_ID = "EQUALITY";92private static final String ORDERING_ID = "ORDERING";93private static final String SUBSTR_ID = "SUBSTR";94private static final String SYNTAX_ID = "SYNTAX";95private static final String SINGLE_VAL_ID = "SINGLE-VALUE";96private static final String COLLECTIVE_ID = "COLLECTIVE";97private static final String NO_USER_MOD_ID = "NO-USER-MODIFICATION";98private static final String USAGE_ID = "USAGE";99100// The string value we give to boolean variables101private static final String SCHEMA_TRUE_VALUE = "true";102103// To get around writing schemas that crash Netscape server104private boolean netscapeBug;105106LdapSchemaParser(boolean netscapeBug) {107this.netscapeBug = netscapeBug;108}109110static final void LDAP2JNDISchema(Attributes schemaAttrs,111LdapSchemaCtx schemaRoot) throws NamingException {112Attribute objectClassesAttr = null;113Attribute attributeDefAttr = null;114Attribute syntaxDefAttr = null;115Attribute matchRuleDefAttr = null;116117objectClassesAttr = schemaAttrs.get(OBJECTCLASSDESC_ATTR_ID);118if(objectClassesAttr != null) {119objectDescs2ClassDefs(objectClassesAttr,schemaRoot);120}121122attributeDefAttr = schemaAttrs.get(ATTRIBUTEDESC_ATTR_ID);123if(attributeDefAttr != null) {124attrDescs2AttrDefs(attributeDefAttr, schemaRoot);125}126127syntaxDefAttr = schemaAttrs.get(SYNTAXDESC_ATTR_ID);128if(syntaxDefAttr != null) {129syntaxDescs2SyntaxDefs(syntaxDefAttr, schemaRoot);130}131132matchRuleDefAttr = schemaAttrs.get(MATCHRULEDESC_ATTR_ID);133if(matchRuleDefAttr != null) {134matchRuleDescs2MatchRuleDefs(matchRuleDefAttr, schemaRoot);135}136}137138private static final DirContext objectDescs2ClassDefs(Attribute objDescsAttr,139LdapSchemaCtx schemaRoot)140throws NamingException {141142NamingEnumeration<?> objDescs;143Attributes objDef;144LdapSchemaCtx classDefTree;145146// create the class def subtree147Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);148attrs.put(CLASS_DEF_ATTRS[0], CLASS_DEF_ATTRS[1]);149classDefTree = schemaRoot.setup(LdapSchemaCtx.OBJECTCLASS_ROOT,150OBJECTCLASS_DEFINITION_NAME, attrs);151152objDescs = objDescsAttr.getAll();153String currentName;154while(objDescs.hasMore()) {155String objDesc = (String)objDescs.next();156try {157Object[] def = desc2Def(objDesc);158currentName = (String) def[0];159objDef = (Attributes) def[1];160classDefTree.setup(LdapSchemaCtx.OBJECTCLASS,161currentName, objDef);162} catch (NamingException ne) {163// error occurred while parsing, ignore current entry164}165}166167return classDefTree;168}169170private static final DirContext attrDescs2AttrDefs(Attribute attributeDescAttr,171LdapSchemaCtx schemaRoot)172throws NamingException {173174NamingEnumeration<?> attrDescs;175Attributes attrDef;176LdapSchemaCtx attrDefTree;177178// create the AttributeDef subtree179Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);180attrs.put(ATTR_DEF_ATTRS[0], ATTR_DEF_ATTRS[1]);181attrDefTree = schemaRoot.setup(LdapSchemaCtx.ATTRIBUTE_ROOT,182ATTRIBUTE_DEFINITION_NAME, attrs);183184attrDescs = attributeDescAttr.getAll();185String currentName;186while(attrDescs.hasMore()) {187String attrDesc = (String)attrDescs.next();188try {189Object[] def = desc2Def(attrDesc);190currentName = (String) def[0];191attrDef = (Attributes) def[1];192attrDefTree.setup(LdapSchemaCtx.ATTRIBUTE,193currentName, attrDef);194} catch (NamingException ne) {195// error occurred while parsing, ignore current entry196}197}198199return attrDefTree;200}201202private static final DirContext syntaxDescs2SyntaxDefs(203Attribute syntaxDescAttr,204LdapSchemaCtx schemaRoot)205throws NamingException {206207NamingEnumeration<?> syntaxDescs;208Attributes syntaxDef;209LdapSchemaCtx syntaxDefTree;210211// create the SyntaxDef subtree212Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);213attrs.put(SYNTAX_DEF_ATTRS[0], SYNTAX_DEF_ATTRS[1]);214syntaxDefTree = schemaRoot.setup(LdapSchemaCtx.SYNTAX_ROOT,215SYNTAX_DEFINITION_NAME, attrs);216217syntaxDescs = syntaxDescAttr.getAll();218String currentName;219while(syntaxDescs.hasMore()) {220String syntaxDesc = (String)syntaxDescs.next();221try {222Object[] def = desc2Def(syntaxDesc);223currentName = (String) def[0];224syntaxDef = (Attributes) def[1];225syntaxDefTree.setup(LdapSchemaCtx.SYNTAX,226currentName, syntaxDef);227} catch (NamingException ne) {228// error occurred while parsing, ignore current entry229}230}231232return syntaxDefTree;233}234235private static final DirContext matchRuleDescs2MatchRuleDefs(236Attribute matchRuleDescAttr,237LdapSchemaCtx schemaRoot)238throws NamingException {239240NamingEnumeration<?> matchRuleDescs;241Attributes matchRuleDef;242LdapSchemaCtx matchRuleDefTree;243244// create the MatchRuleDef subtree245Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);246attrs.put(MATCHRULE_DEF_ATTRS[0], MATCHRULE_DEF_ATTRS[1]);247matchRuleDefTree = schemaRoot.setup(LdapSchemaCtx.MATCHRULE_ROOT,248MATCHRULE_DEFINITION_NAME, attrs);249250matchRuleDescs = matchRuleDescAttr.getAll();251String currentName;252while(matchRuleDescs.hasMore()) {253String matchRuleDesc = (String)matchRuleDescs.next();254try {255Object[] def = desc2Def(matchRuleDesc);256currentName = (String) def[0];257matchRuleDef = (Attributes) def[1];258matchRuleDefTree.setup(LdapSchemaCtx.MATCHRULE,259currentName, matchRuleDef);260} catch (NamingException ne) {261// error occurred while parsing, ignore current entry262}263}264265return matchRuleDefTree;266}267268private static final Object[] desc2Def(String desc)269throws NamingException {270//System.err.println(desc);271272Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);273Attribute attr = null;274int[] pos = new int[]{1}; // tolerate missing leading space275boolean moreTags = true;276277// Always begins with <whsp numericoid whsp>278attr = readNumericOID(desc, pos);279String currentName = (String) attr.get(0); // name is OID by default280attrs.put(attr);281282skipWhitespace(desc, pos);283284while (moreTags) {285attr = readNextTag(desc, pos);286attrs.put(attr);287288if (attr.getID().equals(NAME_ID)) {289currentName = (String) attr.get(0); // use NAME attribute as name290}291292skipWhitespace(desc, pos);293294if( pos[0] >= desc.length() -1 ) {295moreTags = false;296}297}298299return new Object[] {currentName, attrs};300}301302// returns the index of the first whitespace char of a linear whitespace303// sequence ending at the given position.304private static final int findTrailingWhitespace(String string, int pos) {305for(int i = pos; i > 0; i--) {306if(string.charAt(i) != WHSP) {307return i + 1;308}309}310return 0;311}312313private static final void skipWhitespace(String string, int[] pos) {314for(int i=pos[0]; i < string.length(); i++) {315if(string.charAt(i) != WHSP) {316pos[0] = i;317if (debug) {318System.err.println("skipWhitespace: skipping to "+i);319}320return;321}322}323}324325private static final Attribute readNumericOID(String string, int[] pos)326throws NamingException {327328if (debug) {329System.err.println("readNumericoid: pos="+pos[0]);330}331332int begin, end;333String value = null;334335skipWhitespace(string, pos);336337begin = pos[0];338end = string.indexOf(WHSP, begin);339340if (end == -1 || end - begin < 1) {341throw new InvalidAttributeValueException("no numericoid found: "342+ string);343}344345value = string.substring(begin, end);346347pos[0] += value.length();348349return new BasicAttribute(NUMERICOID_ID, value);350}351352private static final Attribute readNextTag(String string, int[] pos)353throws NamingException {354355Attribute attr = null;356String tagName = null;357String[] values = null;358359skipWhitespace(string, pos);360361if (debug) {362System.err.println("readNextTag: pos="+pos[0]);363}364365// get the name and values of the attribute to return366int trailingSpace = string.indexOf( WHSP, pos[0] );367368// tolerate a schema that omits the trailing space369if (trailingSpace < 0) {370tagName = string.substring( pos[0], string.length() - 1);371} else {372tagName = string.substring( pos[0], trailingSpace );373}374375values = readTag(tagName, string, pos);376377// make sure at least one value was returned378if (values.length == 0) {379throw new InvalidAttributeValueException("no values for " +380"attribute \"" +381tagName + "\"");382}383384// create the attribute, using the first value385attr = new BasicAttribute(tagName, values[0]);386387// add other values if there are any388for(int i = 1; i < values.length; i++) {389attr.add(values[i]);390}391392return attr;393}394395private static final String[] readTag(String tag, String string, int[] pos)396throws NamingException {397398if (debug) {399System.err.println("ReadTag: " + tag + " pos="+pos[0]);400}401402// move parser past tag name403pos[0] += tag.length();404skipWhitespace(string, pos);405406if (tag.equals(NAME_ID)) {407return readQDescrs(string, pos); // names[0] is NAME408}409410if(tag.equals(DESC_ID)) {411return readQDString(string, pos);412}413414if (415tag.equals(EQUALITY_ID) ||416tag.equals(ORDERING_ID) ||417tag.equals(SUBSTR_ID) ||418tag.equals(SYNTAX_ID)) {419return readWOID(string, pos);420}421422if (tag.equals(OBSOLETE_ID) ||423tag.equals(ABSTRACT_ID) ||424tag.equals(STRUCTURAL_ID) ||425tag.equals(AUXILIARY_ID) ||426tag.equals(SINGLE_VAL_ID) ||427tag.equals(COLLECTIVE_ID) ||428tag.equals(NO_USER_MOD_ID)) {429return new String[] {SCHEMA_TRUE_VALUE};430}431432if (tag.equals(SUP_ID) || // oid list for object class; WOID for attribute433tag.equals(MUST_ID) ||434tag.equals(MAY_ID) ||435tag.equals(USAGE_ID)) {436return readOIDs(string, pos);437}438439// otherwise it's a schema element with a quoted string value440return readQDStrings(string, pos);441}442443private static final String[] readQDString(String string, int[] pos)444throws NamingException {445446int begin, end;447448begin = string.indexOf(SINGLE_QUOTE, pos[0]) + 1;449end = string.indexOf(SINGLE_QUOTE, begin);450451if (debug) {452System.err.println("ReadQDString: pos=" + pos[0] +453" begin=" + begin + " end=" + end);454}455456if(begin == -1 || end == -1 || begin == end) {457throw new InvalidAttributeIdentifierException("malformed " +458"QDString: " +459string);460}461462// make sure the qdstring end symbol is there463if (string.charAt(begin - 1) != SINGLE_QUOTE) {464throw new InvalidAttributeIdentifierException("qdstring has " +465"no end mark: " +466string);467}468469pos[0] = end+1;470return new String[] {string.substring(begin, end)};471}472473/**474* dstring = 1*utf8475* qdstring = whsp "'" dstring "'" whsp476* qdstringlist = [ qdstring *( qdstring ) ]477* qdstrings = qdstring / ( whsp "(" qdstringlist ")" whsp )478*/479private static final String[] readQDStrings(String string, int[] pos)480throws NamingException {481482return readQDescrs(string, pos);483}484485/**486* ; object descriptors used as schema element names487* qdescrs = qdescr / ( whsp "(" qdescrlist ")" whsp )488* qdescrlist = [ qdescr *( qdescr ) ]489* qdescr = whsp "'" descr "'" whsp490* descr = keystring491*/492private static final String[] readQDescrs(String string, int[] pos)493throws NamingException {494495if (debug) {496System.err.println("readQDescrs: pos="+pos[0]);497}498499skipWhitespace(string, pos);500501switch( string.charAt(pos[0]) ) {502case OID_LIST_BEGIN:503return readQDescrList(string, pos);504case SINGLE_QUOTE:505return readQDString(string, pos);506default:507throw new InvalidAttributeValueException("unexpected oids " +508"string: " + string);509}510}511512/**513* qdescrlist = [ qdescr *( qdescr ) ]514* qdescr = whsp "'" descr "'" whsp515* descr = keystring516*/517private static final String[] readQDescrList(String string, int[] pos)518throws NamingException {519520int begin, end;521Vector<String> values = new Vector<>(5);522523if (debug) {524System.err.println("ReadQDescrList: pos="+pos[0]);525}526527pos[0]++; // skip '('528skipWhitespace(string, pos);529begin = pos[0];530end = string.indexOf(OID_LIST_END, begin);531532if(end == -1) {533throw new InvalidAttributeValueException ("oidlist has no end "+534"mark: " + string);535}536537while(begin < end) {538String[] one = readQDString(string, pos);539540if (debug) {541System.err.println("ReadQDescrList: found '" + one[0] +542"' at begin=" + begin + " end =" + end);543}544545values.addElement(one[0]);546skipWhitespace(string, pos);547begin = pos[0];548}549550pos[0] = end+1; // skip ')'551552String[] answer = new String[values.size()];553for (int i = 0; i < answer.length; i++) {554answer[i] = values.elementAt(i);555}556return answer;557}558559private static final String[] readWOID(String string, int[] pos)560throws NamingException {561562if (debug) {563System.err.println("readWOIDs: pos="+pos[0]);564}565566skipWhitespace(string, pos);567568if (string.charAt(pos[0]) == SINGLE_QUOTE) {569// %%% workaround for Netscape schema bug570return readQDString(string, pos);571}572573int begin, end;574575begin = pos[0];576end = string.indexOf(WHSP, begin);577578if (debug) {579System.err.println("ReadWOID: pos=" + pos[0] +580" begin=" + begin + " end=" + end);581}582583if(end == -1 || begin == end) {584throw new InvalidAttributeIdentifierException("malformed " +585"OID: " +586string);587}588pos[0] = end+1;589590return new String[] {string.substring(begin, end)};591}592593/*594* oids = woid / ( "(" oidlist ")" )595* oidlist = woid *( "$" woid )596*/597private static final String[] readOIDs(String string, int[] pos)598throws NamingException {599600if (debug) {601System.err.println("readOIDs: pos="+pos[0]);602}603604skipWhitespace(string, pos);605606// Single OID607if (string.charAt(pos[0]) != OID_LIST_BEGIN) {608return readWOID(string, pos);609}610611// Multiple OIDs612613int begin, cur, end;614String oidName = null;615Vector<String> values = new Vector<>(5);616617if (debug) {618System.err.println("ReadOIDList: pos="+pos[0]);619}620621pos[0]++;622skipWhitespace(string, pos);623begin = pos[0];624end = string.indexOf(OID_LIST_END, begin);625cur = string.indexOf(OID_SEPARATOR, begin);626627if(end == -1) {628throw new InvalidAttributeValueException ("oidlist has no end "+629"mark: " + string);630}631632if(cur == -1 || end < cur) {633cur = end;634}635636while(cur < end && cur > 0) {637int wsBegin = findTrailingWhitespace(string, cur - 1);638oidName = string.substring(begin, wsBegin);639if (debug) {640System.err.println("ReadOIDList: found '" + oidName +641"' at begin=" + begin + " end =" + end);642}643values.addElement(oidName);644pos[0] = cur + 1;645skipWhitespace(string, pos);646begin = pos[0];647cur = string.indexOf(OID_SEPARATOR, begin);648if(debug) {System.err.println("ReadOIDList: begin = " + begin);}649}650651if (debug) {652System.err.println("ReadOIDList: found '" + oidName +653"' at begin=" + begin + " end =" + end);654}655656int wsBegin = findTrailingWhitespace(string, end - 1);657oidName = string.substring(begin, wsBegin);658values.addElement(oidName);659660pos[0] = end+1;661662String[] answer = new String[values.size()];663for (int i = 0; i < answer.length; i++) {664answer[i] = values.elementAt(i);665}666return answer;667}668669// ----------------- "unparser" methods670// Methods that are used for translating a node in the schema tree671// into RFC2252 format for storage back into the LDAP directory672/*673static Attributes JNDI2LDAPSchema(DirContext schemaRoot)674throws NamingException {675676Attribute objDescAttr = new BasicAttribute(OBJECTCLASSDESC_ATTR_ID);677Attribute attrDescAttr = new BasicAttribute(ATTRIBUTEDESC_ATTR_ID);678Attribute syntaxDescAttr = new BasicAttribute(SYNTAXDESC_ATTR_ID);679Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);680DirContext classDefs, attributeDefs, syntaxDefs;681Attributes classDefsAttrs, attributeDefsAttrs, syntaxDefsAttrs;682NamingEnumeration defs;683Object obj;684int i = 0;685686try {687obj = schemaRoot.lookup(OBJECTCLASS_DEFINITION_NAME);688if(obj != null && obj instanceof DirContext) {689classDefs = (DirContext)obj;690defs = classDefs.listBindings("");691while(defs.hasMoreElements()) {692i++;693DirContext classDef = (DirContext)694((Binding)(defs.next())).getObject();695classDefAttrs = classDef.getAttributes("");696objDescAttr.add(classDef2ObjectDesc(classDefAttrs));697}698if (debug)699System.err.println(i + " total object classes");700attrs.put(objDescAttr);701} else {702throw new NamingException(703"Problem with Schema tree: the object named " +704OBJECTCLASS_DEFINITION_NAME + " is not a " +705"DirContext");706}707} catch (NameNotFoundException e) {} // ignore708709i=0;710try {711obj = schemaRoot.lookup(ATTRIBUTE_DEFINITION_NAME);712if(obj instanceof DirContext) {713attributeDefs = (DirContext)obj;714defs = attributeDefs.listBindings("");715while(defs.hasMoreElements()) {716i++;717DirContext attrDef = (DirContext)718((Binding)defs.next()).getObject();719attrDefAttrs = attrDef.getAttributes("");720attrDescAttr.add(attrDef2AttrDesc(attrDefAttrs));721}722if (debug)723System.err.println(i + " attribute definitions");724attrs.put(attrDescAttr);725} else {726throw new NamingException(727"Problem with schema tree: the object named " +728ATTRIBUTE_DEFINITION_NAME + " is not a " +729"DirContext");730}731} catch (NameNotFoundException e) {} // ignore732733i=0;734try {735obj = schemaRoot.lookup(SYNTAX_DEFINITION_NAME);736if(obj instanceof DirContext) {737syntaxDefs = (DirContext)obj;738defs =syntaxDefs.listBindings("");739while(defs.hasMoreElements()) {740i++;741DirContext syntaxDef = (DirContext)742((Binding)defs.next()).getObject();743syntaxDefAttrs = syntaxDef.getAttributes("");744syntaxDescAttr.add(syntaxDef2SyntaxDesc(syntaxDefAttrs));745}746if (debug)747System.err.println(i + " total syntax definitions");748attrs.put(syntaxDescAttr);749} else {750throw new NamingException(751"Problem with schema tree: the object named " +752SYNTAX_DEFINITION_NAME + " is not a " +753"DirContext");754}755} catch (NameNotFoundException e) {} // ignore756757return attrs;758}759760*/761762/**763* Translate attributes that describe an object class into the764* string description as defined in RFC 2252.765*/766private final String classDef2ObjectDesc(Attributes attrs)767throws NamingException {768769StringBuilder objectDesc = new StringBuilder("( ");770771Attribute attr = null;772int count = 0;773774// extract attributes by ID to guarantee ordering775776attr = attrs.get(NUMERICOID_ID);777if (attr != null) {778objectDesc.append(writeNumericOID(attr));779count++;780} else {781throw new ConfigurationException("Class definition doesn't" +782"have a numeric OID");783}784785attr = attrs.get(NAME_ID);786if (attr != null) {787objectDesc.append(writeQDescrs(attr));788count++;789}790791attr = attrs.get(DESC_ID);792if (attr != null) {793objectDesc.append(writeQDString(attr));794count++;795}796797attr = attrs.get(OBSOLETE_ID);798if (attr != null) {799objectDesc.append(writeBoolean(attr));800count++;801}802803attr = attrs.get(SUP_ID);804if (attr != null) {805objectDesc.append(writeOIDs(attr));806count++;807}808809attr = attrs.get(ABSTRACT_ID);810if (attr != null) {811objectDesc.append(writeBoolean(attr));812count++;813}814815attr = attrs.get(STRUCTURAL_ID);816if (attr != null) {817objectDesc.append(writeBoolean(attr));818count++;819}820821attr = attrs.get(AUXILIARY_ID);822if (attr != null) {823objectDesc.append(writeBoolean(attr));824count++;825}826827attr = attrs.get(MUST_ID);828if (attr != null) {829objectDesc.append(writeOIDs(attr));830count++;831}832833attr = attrs.get(MAY_ID);834if (attr != null) {835objectDesc.append(writeOIDs(attr));836count++;837}838839// process any remaining attributes840if (count < attrs.size()) {841String attrId = null;842843// use enumeration because attribute ID is not known844for (NamingEnumeration<? extends Attribute> ae = attrs.getAll();845ae.hasMoreElements(); ) {846847attr = ae.next();848attrId = attr.getID();849850// skip those already processed851if (attrId.equals(NUMERICOID_ID) ||852attrId.equals(NAME_ID) ||853attrId.equals(SUP_ID) ||854attrId.equals(MAY_ID) ||855attrId.equals(MUST_ID) ||856attrId.equals(STRUCTURAL_ID) ||857attrId.equals(DESC_ID) ||858attrId.equals(AUXILIARY_ID) ||859attrId.equals(ABSTRACT_ID) ||860attrId.equals(OBSOLETE_ID)) {861continue;862863} else {864objectDesc.append(writeQDStrings(attr));865}866}867}868869objectDesc.append(")");870871return objectDesc.toString();872}873874/**875* Translate attributes that describe an attribute definition into the876* string description as defined in RFC 2252.877*/878private final String attrDef2AttrDesc(Attributes attrs)879throws NamingException {880881StringBuilder attrDesc = new StringBuilder("( "); // opening parens882883Attribute attr = null;884int count = 0;885886// extract attributes by ID to guarantee ordering887888attr = attrs.get(NUMERICOID_ID);889if (attr != null) {890attrDesc.append(writeNumericOID(attr));891count++;892} else {893throw new ConfigurationException("Attribute type doesn't" +894"have a numeric OID");895}896897attr = attrs.get(NAME_ID);898if (attr != null) {899attrDesc.append(writeQDescrs(attr));900count++;901}902903attr = attrs.get(DESC_ID);904if (attr != null) {905attrDesc.append(writeQDString(attr));906count++;907}908909attr = attrs.get(OBSOLETE_ID);910if (attr != null) {911attrDesc.append(writeBoolean(attr));912count++;913}914915attr = attrs.get(SUP_ID);916if (attr != null) {917attrDesc.append(writeWOID(attr));918count++;919}920921attr = attrs.get(EQUALITY_ID);922if (attr != null) {923attrDesc.append(writeWOID(attr));924count++;925}926927attr = attrs.get(ORDERING_ID);928if (attr != null) {929attrDesc.append(writeWOID(attr));930count++;931}932933attr = attrs.get(SUBSTR_ID);934if (attr != null) {935attrDesc.append(writeWOID(attr));936count++;937}938939attr = attrs.get(SYNTAX_ID);940if (attr != null) {941attrDesc.append(writeWOID(attr));942count++;943}944945attr = attrs.get(SINGLE_VAL_ID);946if (attr != null) {947attrDesc.append(writeBoolean(attr));948count++;949}950951attr = attrs.get(COLLECTIVE_ID);952if (attr != null) {953attrDesc.append(writeBoolean(attr));954count++;955}956957attr = attrs.get(NO_USER_MOD_ID);958if (attr != null) {959attrDesc.append(writeBoolean(attr));960count++;961}962963attr = attrs.get(USAGE_ID);964if (attr != null) {965attrDesc.append(writeQDString(attr));966count++;967}968969// process any remaining attributes970if (count < attrs.size()) {971String attrId = null;972973// use enumeration because attribute ID is not known974for (NamingEnumeration<? extends Attribute> ae = attrs.getAll();975ae.hasMoreElements(); ) {976977attr = ae.next();978attrId = attr.getID();979980// skip those already processed981if (attrId.equals(NUMERICOID_ID) ||982attrId.equals(NAME_ID) ||983attrId.equals(SYNTAX_ID) ||984attrId.equals(DESC_ID) ||985attrId.equals(SINGLE_VAL_ID) ||986attrId.equals(EQUALITY_ID) ||987attrId.equals(ORDERING_ID) ||988attrId.equals(SUBSTR_ID) ||989attrId.equals(NO_USER_MOD_ID) ||990attrId.equals(USAGE_ID) ||991attrId.equals(SUP_ID) ||992attrId.equals(COLLECTIVE_ID) ||993attrId.equals(OBSOLETE_ID)) {994continue;995996} else {997attrDesc.append(writeQDStrings(attr));998}999}1000}10011002attrDesc.append(")"); // add closing parens10031004return attrDesc.toString();1005}10061007/**1008* Translate attributes that describe an attribute syntax definition into the1009* string description as defined in RFC 2252.1010*/1011private final String syntaxDef2SyntaxDesc(Attributes attrs)1012throws NamingException {10131014StringBuilder syntaxDesc = new StringBuilder("( "); // opening parens10151016Attribute attr = null;1017int count = 0;10181019// extract attributes by ID to guarantee ordering10201021attr = attrs.get(NUMERICOID_ID);1022if (attr != null) {1023syntaxDesc.append(writeNumericOID(attr));1024count++;1025} else {1026throw new ConfigurationException("Attribute type doesn't" +1027"have a numeric OID");1028}10291030attr = attrs.get(DESC_ID);1031if (attr != null) {1032syntaxDesc.append(writeQDString(attr));1033count++;1034}10351036// process any remaining attributes1037if (count < attrs.size()) {1038String attrId = null;10391040// use enumeration because attribute ID is not known1041for (NamingEnumeration<? extends Attribute> ae = attrs.getAll();1042ae.hasMoreElements(); ) {10431044attr = ae.next();1045attrId = attr.getID();10461047// skip those already processed1048if (attrId.equals(NUMERICOID_ID) ||1049attrId.equals(DESC_ID)) {1050continue;10511052} else {1053syntaxDesc.append(writeQDStrings(attr));1054}1055}1056}10571058syntaxDesc.append(")");10591060return syntaxDesc.toString();1061}10621063/**1064* Translate attributes that describe an attribute matching rule1065* definition into the string description as defined in RFC 2252.1066*/1067private final String matchRuleDef2MatchRuleDesc(Attributes attrs)1068throws NamingException {10691070StringBuilder matchRuleDesc = new StringBuilder("( "); // opening parens10711072Attribute attr = null;1073int count = 0;10741075// extract attributes by ID to guarantee ordering10761077attr = attrs.get(NUMERICOID_ID);1078if (attr != null) {1079matchRuleDesc.append(writeNumericOID(attr));1080count++;1081} else {1082throw new ConfigurationException("Attribute type doesn't" +1083"have a numeric OID");1084}10851086attr = attrs.get(NAME_ID);1087if (attr != null) {1088matchRuleDesc.append(writeQDescrs(attr));1089count++;1090}10911092attr = attrs.get(DESC_ID);1093if (attr != null) {1094matchRuleDesc.append(writeQDString(attr));1095count++;1096}10971098attr = attrs.get(OBSOLETE_ID);1099if (attr != null) {1100matchRuleDesc.append(writeBoolean(attr));1101count++;1102}11031104attr = attrs.get(SYNTAX_ID);1105if (attr != null) {1106matchRuleDesc.append(writeWOID(attr));1107count++;1108} else {1109throw new ConfigurationException("Attribute type doesn't" +1110"have a syntax OID");1111}11121113// process any remaining attributes1114if (count < attrs.size()) {1115String attrId = null;11161117// use enumeration because attribute ID is not known1118for (NamingEnumeration<? extends Attribute> ae = attrs.getAll();1119ae.hasMoreElements(); ) {11201121attr = ae.next();1122attrId = attr.getID();11231124// skip those already processed1125if (attrId.equals(NUMERICOID_ID) ||1126attrId.equals(NAME_ID) ||1127attrId.equals(SYNTAX_ID) ||1128attrId.equals(DESC_ID) ||1129attrId.equals(OBSOLETE_ID)) {1130continue;11311132} else {1133matchRuleDesc.append(writeQDStrings(attr));1134}1135}1136}11371138matchRuleDesc.append(")");11391140return matchRuleDesc.toString();1141}11421143private final String writeNumericOID(Attribute nOIDAttr)1144throws NamingException {1145if(nOIDAttr.size() != 1) {1146throw new InvalidAttributeValueException(1147"A class definition must have exactly one numeric OID");1148}1149return (String)(nOIDAttr.get()) + WHSP;1150}11511152private final String writeWOID(Attribute attr) throws NamingException {1153if (netscapeBug)1154return writeQDString(attr);1155else1156return attr.getID() + WHSP + attr.get() + WHSP;1157}11581159/* qdescr = whsp "'" descr "'" whsp */1160private final String writeQDString(Attribute qdStringAttr)1161throws NamingException {1162if(qdStringAttr.size() != 1) {1163throw new InvalidAttributeValueException(1164qdStringAttr.getID() + " must have exactly one value");1165}11661167return qdStringAttr.getID() + WHSP +1168SINGLE_QUOTE + qdStringAttr.get() + SINGLE_QUOTE + WHSP;1169}11701171/**1172* dstring = 1*utf81173* qdstring = whsp "'" dstring "'" whsp1174* qdstringlist = [ qdstring *( qdstring ) ]1175* qdstrings = qdstring / ( whsp "(" qdstringlist ")" whsp )1176*/1177private final String writeQDStrings(Attribute attr) throws NamingException {1178return writeQDescrs(attr);1179}11801181/**1182* qdescrs = qdescr / ( whsp "(" qdescrlist ")" whsp )1183* qdescrlist = [ qdescr *( qdescr ) ]1184* qdescr = whsp "'" descr "'" whsp1185* descr = keystring1186*/1187private final String writeQDescrs(Attribute attr) throws NamingException {1188switch(attr.size()) {1189case 0:1190throw new InvalidAttributeValueException(1191attr.getID() + "has no values");1192case 1:1193return writeQDString(attr);1194}11951196// write QDList11971198StringBuilder qdList = new StringBuilder(attr.getID());1199qdList.append(WHSP);1200qdList.append(OID_LIST_BEGIN);12011202NamingEnumeration<?> values = attr.getAll();12031204while(values.hasMore()) {1205qdList.append(WHSP);1206qdList.append(SINGLE_QUOTE);1207qdList.append((String)values.next());1208qdList.append(SINGLE_QUOTE);1209qdList.append(WHSP);1210}12111212qdList.append(OID_LIST_END);1213qdList.append(WHSP);12141215return qdList.toString();1216}12171218private final String writeOIDs(Attribute oidsAttr)1219throws NamingException {12201221switch(oidsAttr.size()) {1222case 0:1223throw new InvalidAttributeValueException(1224oidsAttr.getID() + "has no values");12251226case 1:1227if (netscapeBug) {1228break; // %%% write out as list to avoid crashing server1229}1230return writeWOID(oidsAttr);1231}12321233// write OID List12341235StringBuilder oidList = new StringBuilder(oidsAttr.getID());1236oidList.append(WHSP);1237oidList.append(OID_LIST_BEGIN);12381239NamingEnumeration<?> values = oidsAttr.getAll();1240oidList.append(WHSP);1241oidList.append(values.next());12421243while(values.hasMore()) {1244oidList.append(WHSP);1245oidList.append(OID_SEPARATOR);1246oidList.append(WHSP);1247oidList.append((String)values.next());1248}12491250oidList.append(WHSP);1251oidList.append(OID_LIST_END);1252oidList.append(WHSP);12531254return oidList.toString();1255}12561257private final String writeBoolean(Attribute booleanAttr)1258throws NamingException {1259return booleanAttr.getID() + WHSP;1260}12611262/**1263* Returns an attribute for updating the Object Class Definition schema1264* attribute1265*/1266final Attribute stringifyObjDesc(Attributes classDefAttrs)1267throws NamingException {1268Attribute objDescAttr = new BasicAttribute(OBJECTCLASSDESC_ATTR_ID);1269objDescAttr.add(classDef2ObjectDesc(classDefAttrs));1270return objDescAttr;1271}12721273/**1274* Returns an attribute for updating the Attribute Definition schema attribute1275*/1276final Attribute stringifyAttrDesc(Attributes attrDefAttrs)1277throws NamingException {1278Attribute attrDescAttr = new BasicAttribute(ATTRIBUTEDESC_ATTR_ID);1279attrDescAttr.add(attrDef2AttrDesc(attrDefAttrs));1280return attrDescAttr;1281}12821283/**1284* Returns an attribute for updating the Syntax schema attribute1285*/1286final Attribute stringifySyntaxDesc(Attributes syntaxDefAttrs)1287throws NamingException {1288Attribute syntaxDescAttr = new BasicAttribute(SYNTAXDESC_ATTR_ID);1289syntaxDescAttr.add(syntaxDef2SyntaxDesc(syntaxDefAttrs));1290return syntaxDescAttr;1291}12921293/**1294* Returns an attribute for updating the Matching Rule schema attribute1295*/1296final Attribute stringifyMatchRuleDesc(Attributes matchRuleDefAttrs)1297throws NamingException {1298Attribute matchRuleDescAttr = new BasicAttribute(MATCHRULEDESC_ATTR_ID);1299matchRuleDescAttr.add(matchRuleDef2MatchRuleDesc(matchRuleDefAttrs));1300return matchRuleDescAttr;1301}1302}130313041305