Source Code Cross Referenced for LdifReader.java in  » Scripting » scriptella » scriptella » driver » ldap » ldif » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Scripting » scriptella » scriptella.driver.ldap.ldif 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         *   Copyright 2006 The Apache Software Foundation
0003:         *
0004:         *   Licensed under the Apache License, Version 2.0 (the "License");
0005:         *   you may not use this file except in compliance with the License.
0006:         *   You may obtain a copy of the License at
0007:         *
0008:         *       http://www.apache.org/licenses/LICENSE-2.0
0009:         *
0010:         *   Unless required by applicable law or agreed to in writing, software
0011:         *   distributed under the License is distributed on an "AS IS" BASIS,
0012:         *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013:         *   See the License for the specific language governing permissions and
0014:         *   limitations under the License.
0015:         *
0016:         */
0017:        package scriptella.driver.ldap.ldif;
0018:
0019:        import scriptella.expression.LineIterator;
0020:        import scriptella.util.StringUtils;
0021:
0022:        import javax.naming.directory.DirContext;
0023:        import javax.naming.ldap.BasicControl;
0024:        import javax.naming.ldap.Control;
0025:        import java.io.IOException;
0026:        import java.io.InputStream;
0027:        import java.io.StringReader;
0028:        import java.io.UnsupportedEncodingException;
0029:        import java.net.URL;
0030:        import java.util.ArrayList;
0031:        import java.util.Iterator;
0032:        import java.util.List;
0033:        import java.util.NoSuchElementException;
0034:        import java.util.regex.Matcher;
0035:        import java.util.regex.Pattern;
0036:
0037:        /**
0038:         * <pre>
0039:         *  &lt;ldif-file&gt; ::= &quot;version:&quot; &lt;fill&gt; &lt;number&gt; &lt;seps&gt; &lt;dn-spec&gt; &lt;sep&gt; &lt;ldif-content-change&gt;
0040:         * <p/>
0041:         *  &lt;ldif-content-change&gt; ::=
0042:         *    &lt;number&gt; &lt;oid&gt; &lt;options-e&gt; &lt;value-spec&gt; &lt;sep&gt; &lt;attrval-specs-e&gt; &lt;ldif-attrval-record-e&gt; |
0043:         *    &lt;alpha&gt; &lt;chars-e&gt; &lt;options-e&gt; &lt;value-spec&gt; &lt;sep&gt; &lt;attrval-specs-e&gt; &lt;ldif-attrval-record-e&gt; |
0044:         *    &quot;control:&quot; &lt;fill&gt; &lt;number&gt; &lt;oid&gt; &lt;spaces-e&gt; &lt;criticality&gt; &lt;value-spec-e&gt; &lt;sep&gt; &lt;controls-e&gt;
0045:         *        &quot;changetype:&quot; &lt;fill&gt; &lt;changerecord-type&gt; &lt;ldif-change-record-e&gt; |
0046:         *    &quot;changetype:&quot; &lt;fill&gt; &lt;changerecord-type&gt; &lt;ldif-change-record-e&gt;
0047:         * <p/>
0048:         *  &lt;ldif-attrval-record-e&gt; ::= &lt;seps&gt; &lt;dn-spec&gt; &lt;sep&gt; &lt;attributeType&gt;
0049:         *    &lt;options-e&gt; &lt;value-spec&gt; &lt;sep&gt; &lt;attrval-specs-e&gt;
0050:         *    &lt;ldif-attrval-record-e&gt; | e
0051:         * <p/>
0052:         *  &lt;ldif-change-record-e&gt; ::= &lt;seps&gt; &lt;dn-spec&gt; &lt;sep&gt; &lt;controls-e&gt;
0053:         *    &quot;changetype:&quot; &lt;fill&gt; &lt;changerecord-type&gt; &lt;ldif-change-record-e&gt; | e
0054:         * <p/>
0055:         *  &lt;dn-spec&gt; ::= &quot;dn:&quot; &lt;fill&gt; &lt;safe-string&gt; | &quot;dn::&quot; &lt;fill&gt; &lt;base64-string&gt;
0056:         * <p/>
0057:         *  &lt;controls-e&gt; ::= &quot;control:&quot; &lt;fill&gt; &lt;number&gt; &lt;oid&gt; &lt;spaces-e&gt; &lt;criticality&gt;
0058:         *    &lt;value-spec-e&gt; &lt;sep&gt; &lt;controls-e&gt; | e
0059:         * <p/>
0060:         *  &lt;criticality&gt; ::= &quot;true&quot; | &quot;false&quot; | e
0061:         * <p/>
0062:         *  &lt;oid&gt; ::= '.' &lt;number&gt; &lt;oid&gt; | e
0063:         * <p/>
0064:         *  &lt;attrval-specs-e&gt; ::= &lt;number&gt; &lt;oid&gt; &lt;options-e&gt; &lt;value-spec&gt; &lt;sep&gt; &lt;attrval-specs-e&gt; |
0065:         *    &lt;alpha&gt; &lt;chars-e&gt; &lt;options-e&gt; &lt;value-spec&gt; &lt;sep&gt; &lt;attrval-specs-e&gt; | e
0066:         * <p/>
0067:         *  &lt;value-spec-e&gt; ::= &lt;value-spec&gt; | e
0068:         * <p/>
0069:         *  &lt;value-spec&gt; ::= ':' &lt;fill&gt; &lt;safe-string-e&gt; |
0070:         *    &quot;::&quot; &lt;fill&gt; &lt;base64-chars&gt; |
0071:         *    &quot;:&lt;&quot; &lt;fill&gt; &lt;url&gt;
0072:         * <p/>
0073:         *  &lt;attributeType&gt; ::= &lt;number&gt; &lt;oid&gt; | &lt;alpha&gt; &lt;chars-e&gt;
0074:         * <p/>
0075:         *  &lt;options-e&gt; ::= ';' &lt;char&gt; &lt;chars-e&gt; &lt;options-e&gt; |e
0076:         * <p/>
0077:         *  &lt;chars-e&gt; ::= &lt;char&gt; &lt;chars-e&gt; |  e
0078:         * <p/>
0079:         *  &lt;changerecord-type&gt; ::= &quot;add&quot; &lt;sep&gt; &lt;attributeType&gt; &lt;options-e&gt; &lt;value-spec&gt; &lt;sep&gt; &lt;attrval-specs-e&gt; |
0080:         *    &quot;delete&quot; &lt;sep&gt; |
0081:         *    &quot;modify&quot; &lt;sep&gt; &lt;mod-type&gt; &lt;fill&gt; &lt;attributeType&gt; &lt;options-e&gt; &lt;sep&gt; &lt;attrval-specs-e&gt; &lt;sep&gt; '-' &lt;sep&gt; &lt;mod-specs-e&gt; |
0082:         *    &quot;moddn&quot; &lt;sep&gt; &lt;newrdn&gt; &lt;sep&gt; &quot;deleteoldrdn:&quot; &lt;fill&gt; &lt;0-1&gt; &lt;sep&gt; &lt;newsuperior-e&gt; &lt;sep&gt; |
0083:         *    &quot;modrdn&quot; &lt;sep&gt; &lt;newrdn&gt; &lt;sep&gt; &quot;deleteoldrdn:&quot; &lt;fill&gt; &lt;0-1&gt; &lt;sep&gt; &lt;newsuperior-e&gt; &lt;sep&gt;
0084:         * <p/>
0085:         *  &lt;newrdn&gt; ::= ':' &lt;fill&gt; &lt;safe-string&gt; | &quot;::&quot; &lt;fill&gt; &lt;base64-chars&gt;
0086:         * <p/>
0087:         *  &lt;newsuperior-e&gt; ::= &quot;newsuperior&quot; &lt;newrdn&gt; | e
0088:         * <p/>
0089:         *  &lt;mod-specs-e&gt; ::= &lt;mod-type&gt; &lt;fill&gt; &lt;attributeType&gt; &lt;options-e&gt;
0090:         *    &lt;sep&gt; &lt;attrval-specs-e&gt; &lt;sep&gt; '-' &lt;sep&gt; &lt;mod-specs-e&gt; | e
0091:         * <p/>
0092:         *  &lt;mod-type&gt; ::= &quot;add:&quot; | &quot;delete:&quot; | &quot;replace:&quot;
0093:         * <p/>
0094:         *  &lt;url&gt; ::= &lt;a Uniform Resource Locator, as defined in [6]&gt;
0095:         * <p/>
0096:         * <p/>
0097:         * <p/>
0098:         *  LEXICAL
0099:         *  -------
0100:         * <p/>
0101:         *  &lt;fill&gt;           ::= ' ' &lt;fill&gt; | e
0102:         *  &lt;char&gt;           ::= &lt;alpha&gt; | &lt;digit&gt; | '-'
0103:         *  &lt;number&gt;         ::= &lt;digit&gt; &lt;digits&gt;
0104:         *  &lt;0-1&gt;            ::= '0' | '1'
0105:         *  &lt;digits&gt;         ::= &lt;digit&gt; &lt;digits&gt; | e
0106:         *  &lt;digit&gt;          ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
0107:         *  &lt;seps&gt;           ::= &lt;sep&gt; &lt;seps-e&gt;
0108:         *  &lt;seps-e&gt;         ::= &lt;sep&gt; &lt;seps-e&gt; | e
0109:         *  &lt;sep&gt;            ::= 0x0D 0x0A | 0x0A
0110:         *  &lt;spaces&gt;         ::= ' ' &lt;spaces-e&gt;
0111:         *  &lt;spaces-e&gt;       ::= ' ' &lt;spaces-e&gt; | e
0112:         *  &lt;safe-string-e&gt;  ::= &lt;safe-string&gt; | e
0113:         *  &lt;safe-string&gt;    ::= &lt;safe-init-char&gt; &lt;safe-chars&gt;
0114:         *  &lt;safe-init-char&gt; ::= [0x01-0x09] | 0x0B | 0x0C | [0x0E-0x1F] | [0x21-0x39] | 0x3B | [0x3D-0x7F]
0115:         *  &lt;safe-chars&gt;     ::= &lt;safe-char&gt; &lt;safe-chars&gt; | e
0116:         *  &lt;safe-char&gt;      ::= [0x01-0x09] | 0x0B | 0x0C | [0x0E-0x7F]
0117:         *  &lt;base64-string&gt;  ::= &lt;base64-char&gt; &lt;base64-chars&gt;
0118:         *  &lt;base64-chars&gt;   ::= &lt;base64-char&gt; &lt;base64-chars&gt; | e
0119:         *  &lt;base64-char&gt;    ::= 0x2B | 0x2F | [0x30-0x39] | 0x3D | [0x41-9x5A] | [0x61-0x7A]
0120:         *  &lt;alpha&gt;          ::= [0x41-0x5A] | [0x61-0x7A]
0121:         * <p/>
0122:         *  COMMENTS
0123:         *  --------
0124:         *  - The ldap-oid VN is not correct in the RFC-2849. It has been changed from 1*DIGIT 0*1(&quot;.&quot; 1*DIGIT) to
0125:         *  DIGIT+ (&quot;.&quot; DIGIT+)*
0126:         *  - The mod-spec lacks a sep between *attrval-spec and &quot;-&quot;.
0127:         *  - The BASE64-UTF8-STRING should be BASE64-CHAR BASE64-STRING
0128:         *  - The ValueSpec rule must accept multilines values. In this case, we have a LF followed by a
0129:         *  single space before the continued value.
0130:         * </pre>
0131:         */
0132:        public class LdifReader implements  Iterator<Entry>, Iterable<Entry> {
0133:
0134:            /**
0135:             * A list of read lines
0136:             */
0137:            private final List<String> lines = new ArrayList<String>();
0138:
0139:            /**
0140:             * The ldif file version default value
0141:             */
0142:            private static final int DEFAULT_VERSION = 1;
0143:
0144:            /**
0145:             * The ldif version
0146:             */
0147:            private int version = DEFAULT_VERSION;
0148:
0149:            /**
0150:             * Type of element read
0151:             */
0152:            private static final int ENTRY = 0;
0153:
0154:            private static final int CHANGE = 1;
0155:
0156:            private static final int UNKNOWN = 2;
0157:
0158:            /**
0159:             * Size limit for file contained values
0160:             */
0161:            private final long sizeLimit;
0162:
0163:            /**
0164:             * The default size limit : 1Mo
0165:             */
0166:            private static final long SIZE_LIMIT_DEFAULT = 1024000;
0167:
0168:            /**
0169:             * State values for the modify operation
0170:             */
0171:            private static final int MOD_SPEC = 0;
0172:
0173:            private static final int ATTRVAL_SPEC = 1;
0174:
0175:            private static final int ATTRVAL_SPEC_OR_SEP = 2;
0176:
0177:            /**
0178:             * Iterator prefetched entry
0179:             */
0180:            private Entry prefetched;
0181:
0182:            /**
0183:             * The ldif LineIterator
0184:             */
0185:            private LineIterator in;
0186:
0187:            /**
0188:             * A flag set if the ldif contains entries
0189:             */
0190:            private boolean containsEntries;
0191:
0192:            /**
0193:             * A flag set if the ldif contains changes
0194:             */
0195:            private boolean containsChanges;
0196:
0197:            /**
0198:             * A constructor which takes a line iterator.
0199:             *
0200:             * @param in A Reader containing ldif formated input
0201:             * @throws LdifParseException If the file cannot be processed or if the format is incorrect
0202:             */
0203:            public LdifReader(LineIterator in) {
0204:                this (in, SIZE_LIMIT_DEFAULT);
0205:            }
0206:
0207:            /**
0208:             * * A constructor which takes a line iterator and a size limit.
0209:             *
0210:             * @param in A Reader containing ldif formated input
0211:             * @param sizeLimit maximum file size that can be accepted for an attribute value
0212:             */
0213:            public LdifReader(LineIterator in, long sizeLimit) {
0214:                this .in = in;
0215:                this .sizeLimit = sizeLimit;
0216:                // First get the version - if any -
0217:                version = parseVersion();
0218:                prefetched = parseEntry();
0219:            }
0220:
0221:            /**
0222:             * For testing purposes.
0223:             */
0224:            public LdifReader(String string) {
0225:                this (new LineIterator(new StringReader(string)));
0226:            }
0227:
0228:            /**
0229:             * @return The ldif file version
0230:             */
0231:            public int getVersion() {
0232:                return version;
0233:            }
0234:
0235:            /**
0236:             * @return The maximum size of a file which is used into an attribute value.
0237:             */
0238:            public long getSizeLimit() {
0239:                return sizeLimit;
0240:            }
0241:
0242:            /**
0243:             * Parse the changeType
0244:             *
0245:             * @param line The line which contains the changeType
0246:             * @return The operation.
0247:             */
0248:            private int parseChangeType(String line) {
0249:                int operation = Entry.ADD;
0250:
0251:                String modOp = line.substring("changetype:".length() + 1)
0252:                        .trim();
0253:
0254:                if ("add".equalsIgnoreCase(modOp)) {
0255:                    operation = Entry.ADD;
0256:                } else if ("delete".equalsIgnoreCase(modOp)) {
0257:                    operation = Entry.DELETE;
0258:                } else if ("modify".equalsIgnoreCase(modOp)) {
0259:                    operation = Entry.MODIFY;
0260:                } else if ("moddn".equalsIgnoreCase(modOp)) {
0261:                    operation = Entry.MODDN;
0262:                } else if ("modrdn".equalsIgnoreCase(modOp)) {
0263:                    operation = Entry.MODRDN;
0264:                }
0265:
0266:                return operation;
0267:            }
0268:
0269:            /**
0270:             * Parse the DN of an entry
0271:             *
0272:             * @param line The line to parse
0273:             * @return A DN
0274:             */
0275:            private String parseDn(String line) {
0276:                String dn = null;
0277:
0278:                String lowerLine = line.toLowerCase();
0279:
0280:                if (lowerLine.startsWith("dn:") || lowerLine.startsWith("DN:")) {
0281:                    // Ok, we have a DN. Is it base 64 encoded ?
0282:                    int length = line.length();
0283:
0284:                    if (length == 3) {
0285:                        // The DN is empty : error
0286:                        throw new LdifParseException("No DN for entry", line);
0287:                    } else if (line.charAt(3) == ':') {
0288:                        if (length > 4) {
0289:                            // This is a base 64 encoded DN.
0290:                            String trimmedLine = line.substring(4).trim();
0291:
0292:                            try {
0293:                                dn = new String(Utils.base64Decode(trimmedLine
0294:                                        .toCharArray()), "UTF-8");
0295:                            } catch (UnsupportedEncodingException uee) {
0296:                                // The DN is not base 64 encoded
0297:                                throw new LdifParseException(
0298:                                        "Invalid base 64 encoded DN", line);
0299:                            }
0300:                        } else {
0301:                            // The DN is empty : error
0302:                            throw new LdifParseException("No DN for entry",
0303:                                    line);
0304:                        }
0305:                    } else {
0306:                        dn = line.substring(3).trim();
0307:                    }
0308:                } else {
0309:                    throw new LdifParseException("No DN for entry", line);
0310:                }
0311:
0312:                return dn;
0313:            }
0314:
0315:            /**
0316:             * Parse the value part.
0317:             *
0318:             * @param line The line which contains the value
0319:             * @param pos  The starting position in the line
0320:             * @return A String or a byte[], depending of the kind of value we get
0321:             * @throws LdifParseException If something went wrong
0322:             */
0323:            private Object parseValue(String line, int pos) {
0324:                if (line.length() > pos + 1) {
0325:                    char c = line.charAt(pos + 1);
0326:
0327:                    if (c == ':') {
0328:                        String value = line.substring(pos + 2).trim();
0329:
0330:                        return Utils.base64Decode(value.toCharArray());
0331:                    } else if (c == '<') {
0332:                        String urlName = line.substring(pos + 2).trim();
0333:                        try {
0334:                            return Utils.toByteArray(getUriStream(urlName),
0335:                                    sizeLimit);
0336:                        } catch (IOException e) {
0337:                            throw new LdifParseException("Failed to read \""
0338:                                    + urlName + "\" file content", line, e);
0339:                        }
0340:                    } else {
0341:                        return line.substring(pos + 1).trim();
0342:                    }
0343:                } else {
0344:                    return null;
0345:                }
0346:            }
0347:
0348:            /**
0349:             * Resolves URI to URL and returns a content stream.
0350:             * This method just creates a new URL, subclasses may chnange this behaviour.
0351:             * @param uri URI to resolve.
0352:             * @return resolved URL content stream.
0353:             * @throws IOException if an I/O error occurs or URI is malformed.
0354:             */
0355:            protected InputStream getUriStream(String uri) throws IOException {
0356:                return new URL(uri).openStream();
0357:            }
0358:
0359:            /**
0360:             * Parse a control. The grammar is : <control> ::= "control:" <fill>
0361:             * <ldap-oid> <critical-e> <value-spec-e> <sep> <critical-e> ::= <spaces>
0362:             * <boolean> | e <boolean> ::= "true" | "false" <value-spec-e> ::=
0363:             * <value-spec> | e <value-spec> ::= ":" <fill> <SAFE-STRING-e> | "::"
0364:             * <fill> <BASE64-STRING> | ":<" <fill> <url>
0365:             * <p/>
0366:             * It can be read as : "control:" <fill> <ldap-oid> [ " "+ ( "true" |
0367:             * "false") ] [ ":" <fill> <SAFE-STRING-e> | "::" <fill> <BASE64-STRING> | ":<"
0368:             * <fill> <url> ]
0369:             *
0370:             * @param line The line containing the control
0371:             * @return A control
0372:             */
0373:            private Control parseControl(String line) {
0374:                String lowerLine = line.toLowerCase().trim();
0375:                char[] controlValue = line.trim().toCharArray();
0376:                int pos = 0;
0377:                int length = controlValue.length;
0378:
0379:                // Get the <ldap-oid>
0380:                if (pos > length) {
0381:                    // No OID : error !
0382:                    throw new LdifParseException("Bad control, no oid", line);
0383:                }
0384:
0385:                int initPos = pos;
0386:
0387:                while (Utils.isCharASCII(controlValue, pos, '.')
0388:                        || Utils.isDigit(controlValue, pos)) {
0389:                    pos++;
0390:                }
0391:
0392:                if (pos == initPos) {
0393:                    // Not a valid OID !
0394:                    throw new LdifParseException("Bad control, no oid", line);
0395:                }
0396:
0397:                String oid = lowerLine.substring(0, pos);
0398:                boolean criticality = false;
0399:                byte[] controlBytes = null;
0400:
0401:                // Get the criticality, if any
0402:                // Skip the <fill>
0403:                while (Utils.isCharASCII(controlValue, pos, ' ')) {
0404:                    pos++;
0405:                }
0406:
0407:                // Check if we have a "true" or a "false"
0408:                int criticalPos = lowerLine.indexOf(':');
0409:
0410:                int criticalLength = 0;
0411:
0412:                if (criticalPos == -1) {
0413:                    criticalLength = length - pos;
0414:                } else {
0415:                    criticalLength = criticalPos - pos;
0416:                }
0417:
0418:                if ((criticalLength == 4)
0419:                        && ("true".equalsIgnoreCase(lowerLine.substring(pos,
0420:                                pos + 4)))) {
0421:                    criticality = true;
0422:                } else if ((criticalLength == 5)
0423:                        && ("false".equalsIgnoreCase(lowerLine.substring(pos,
0424:                                pos + 5)))) {
0425:                    criticality = false;
0426:                } else if (criticalLength != 0) {
0427:                    // If we have a criticality, it should be either "true" or "false",
0428:                    // nothing else
0429:                    throw new LdifParseException("Bad control criticality",
0430:                            line);
0431:                }
0432:
0433:                if (criticalPos > 0) {
0434:                    // We have a value. It can be a normal value, a base64 encoded value
0435:                    // or a file contained value
0436:                    if (Utils.isCharASCII(controlValue, criticalPos + 1, ':')) {
0437:                        // Base 64 encoded value
0438:                        controlBytes = Utils.base64Decode(line.substring(
0439:                                criticalPos + 2).toCharArray());
0440:                    } else if (Utils.isCharASCII(controlValue, criticalPos + 1,
0441:                            '<')) {
0442:                        // File contained value
0443:                    } else {
0444:                        // Standard value
0445:                        byte[] value = new byte[length - criticalPos - 1];
0446:
0447:                        for (int i = 0; i < length - criticalPos - 1; i++) {
0448:                            value[i] = (byte) controlValue[i + criticalPos + 1];
0449:                        }
0450:
0451:                        controlBytes = value;
0452:                    }
0453:                }
0454:
0455:                return new BasicControl(oid, criticality, controlBytes);
0456:            }
0457:
0458:            /**
0459:             * Parse an AttributeType/AttributeValue
0460:             *
0461:             * @param entry     The entry where to store the value
0462:             * @param line      The line to parse
0463:             */
0464:            public void parseAttributeValue(Entry entry, String line) {
0465:                int colonIndex = line.indexOf(':');
0466:
0467:                String attributeType = line.substring(0, colonIndex);
0468:
0469:                // We should *not* have a DN twice
0470:                if (attributeType.equalsIgnoreCase("dn")) {
0471:                    throw new LdifParseException(
0472:                            "A ldif entry should not have two DN", line);
0473:                }
0474:
0475:                Object attributeValue = parseValue(line, colonIndex);
0476:
0477:                // Update the entry
0478:                entry.addAttribute(attributeType, attributeValue);
0479:            }
0480:
0481:            /**
0482:             * Parse a ModRDN operation
0483:             *
0484:             * @param entry The entry to update
0485:             * @param iter  The lines iterator
0486:             */
0487:            private void parseModRdn(Entry entry, Iterator iter) {
0488:                // We must have two lines : one starting with "newrdn:" or "newrdn::",
0489:                // and the second starting with "deleteoldrdn:"
0490:                if (iter.hasNext()) {
0491:                    String line = (String) iter.next();
0492:                    String lowerLine = line.toLowerCase();
0493:
0494:                    if (lowerLine.startsWith("newrdn::")
0495:                            || lowerLine.startsWith("newrdn:")) {
0496:                        int colonIndex = line.indexOf(':');
0497:                        Object attributeValue = parseValue(line, colonIndex);
0498:                        entry
0499:                                .setNewRdn(attributeValue instanceof  String ? (String) attributeValue
0500:                                        : Utils
0501:                                                .utf8ToString((byte[]) attributeValue));
0502:                    } else {
0503:                        throw new LdifParseException("Bad modrdn operation",
0504:                                line);
0505:                    }
0506:
0507:                } else {
0508:                    throw new LdifParseException(
0509:                            "Bad modrdn operation, no newrdn");
0510:                }
0511:
0512:                if (iter.hasNext()) {
0513:                    String line = (String) iter.next();
0514:                    String lowerLine = line.toLowerCase();
0515:
0516:                    if (lowerLine.startsWith("deleteoldrdn:")) {
0517:                        int colonIndex = line.indexOf(':');
0518:                        Object attributeValue = parseValue(line, colonIndex);
0519:                        entry.setDeleteOldRdn("1".equals(attributeValue));
0520:                    } else {
0521:                        throw new LdifParseException(
0522:                                "Bad modrdn operation, no deleteoldrdn", line);
0523:                    }
0524:                } else {
0525:                    throw new LdifParseException(
0526:                            "Bad modrdn operation, no deleteoldrdn");
0527:                }
0528:
0529:            }
0530:
0531:            /**
0532:             * Parse a modify change type.
0533:             * <p/>
0534:             * The grammar is : <changerecord> ::= "changetype:" FILL "modify" SEP
0535:             * <mod-spec> <mod-specs-e> <mod-spec> ::= "add:" <mod-val> | "delete:"
0536:             * <mod-val-del> | "replace:" <mod-val> <mod-specs-e> ::= <mod-spec>
0537:             * <mod-specs-e> | e <mod-val> ::= FILL ATTRIBUTE-DESCRIPTION SEP
0538:             * ATTRVAL-SPEC <attrval-specs-e> "-" SEP <mod-val-del> ::= FILL
0539:             * ATTRIBUTE-DESCRIPTION SEP <attrval-specs-e> "-" SEP <attrval-specs-e> ::=
0540:             * ATTRVAL-SPEC <attrval-specs> | e *
0541:             *
0542:             * @param entry The entry to feed
0543:             * @param iter  The lines
0544:             */
0545:            private void parseModify(Entry entry, Iterator iter) {
0546:                int state = MOD_SPEC;
0547:                String modified = null;
0548:                int modification = 0;
0549:
0550:                // The following flag is used to deal with empty modifications
0551:                boolean isEmptyValue = true;
0552:
0553:                while (iter.hasNext()) {
0554:                    String line = (String) iter.next();
0555:                    String lowerLine = line.toLowerCase();
0556:
0557:                    if (lowerLine.startsWith("-")) {
0558:                        if (state != ATTRVAL_SPEC_OR_SEP) {
0559:                            throw new LdifParseException(
0560:                                    "Bad modify separator", line);
0561:                        } else {
0562:                            if (isEmptyValue) {
0563:                                // Update the entry
0564:                                entry.addModificationItem(modification,
0565:                                        modified, null);
0566:                            }
0567:
0568:                            state = MOD_SPEC;
0569:                            isEmptyValue = true;
0570:                            continue;
0571:                        }
0572:                    } else if (lowerLine.startsWith("add:")) {
0573:                        if ((state != MOD_SPEC) && (state != ATTRVAL_SPEC)) {
0574:                            throw new LdifParseException("Bad modify state",
0575:                                    line);
0576:                        }
0577:
0578:                        modified = line.substring("add:".length()).trim();
0579:                        modification = DirContext.ADD_ATTRIBUTE;
0580:
0581:                        state = ATTRVAL_SPEC;
0582:                    } else if (lowerLine.startsWith("delete:")) {
0583:                        if ((state != MOD_SPEC) && (state != ATTRVAL_SPEC)) {
0584:                            throw new LdifParseException("Bad modify state",
0585:                                    line);
0586:                        }
0587:
0588:                        modified = line.substring("delete:".length()).trim();
0589:                        modification = DirContext.REMOVE_ATTRIBUTE;
0590:
0591:                        state = ATTRVAL_SPEC_OR_SEP;
0592:                    } else if (lowerLine.startsWith("replace:")) {
0593:                        if ((state != MOD_SPEC) && (state != ATTRVAL_SPEC)) {
0594:                            throw new LdifParseException("Bad modify state",
0595:                                    line);
0596:                        }
0597:
0598:                        modified = line.substring("replace:".length()).trim();
0599:                        modification = DirContext.REPLACE_ATTRIBUTE;
0600:
0601:                        state = ATTRVAL_SPEC_OR_SEP;
0602:                    } else {
0603:                        if ((state != ATTRVAL_SPEC)
0604:                                && (state != ATTRVAL_SPEC_OR_SEP)) {
0605:                            throw new LdifParseException("Bad modify state",
0606:                                    line);
0607:                        }
0608:
0609:                        // A standard AttributeType/AttributeValue pair
0610:                        int colonIndex = line.indexOf(':');
0611:
0612:                        String attributeType = line.substring(0, colonIndex);
0613:
0614:                        if (!attributeType.equals(modified)) {
0615:                            throw new LdifParseException(
0616:                                    "Bad modify attribute", line);
0617:                        }
0618:
0619:                        // We should *not* have a DN twice
0620:                        if (attributeType.equals("dn")) {
0621:                            throw new LdifParseException(
0622:                                    "A ldif entry should not have two DN", line);
0623:                        }
0624:
0625:                        Object attributeValue = parseValue(line, colonIndex);
0626:
0627:                        // Update the entry
0628:                        entry.addModificationItem(modification, attributeType,
0629:                                attributeValue);
0630:                        isEmptyValue = false;
0631:
0632:                        state = ATTRVAL_SPEC_OR_SEP;
0633:                    }
0634:                }
0635:            }
0636:
0637:            /**
0638:             * Parse a change operation. We have to handle different cases depending on
0639:             * the operation. 1) Delete : there should *not* be any line after the
0640:             * "changetype: delete" 2) Add : we must have a list of AttributeType :
0641:             * AttributeValue elements 3) ModDN : we must have two following lines: a
0642:             * "newrdn:" and a "deleteoldrdn:" 4) ModRDN : the very same, but a
0643:             * "newsuperior:" line is expected 5) Modify :
0644:             * <p/>
0645:             * The grammar is : <changerecord> ::= "changetype:" FILL "add" SEP
0646:             * <attrval-spec> <attrval-specs-e> | "changetype:" FILL "delete" |
0647:             * "changetype:" FILL "modrdn" SEP <newrdn> SEP <deleteoldrdn> SEP | // To
0648:             * be checked "changetype:" FILL "moddn" SEP <newrdn> SEP <deleteoldrdn> SEP
0649:             * <newsuperior> SEP | "changetype:" FILL "modify" SEP <mod-spec>
0650:             * <mod-specs-e> <newrdn> ::= "newrdn:" FILL RDN | "newrdn::" FILL
0651:             * BASE64-RDN <deleteoldrdn> ::= "deleteoldrdn:" FILL "0" | "deleteoldrdn:"
0652:             * FILL "1" <newsuperior> ::= "newsuperior:" FILL DN | "newsuperior::" FILL
0653:             * BASE64-DN <mod-specs-e> ::= <mod-spec> <mod-specs-e> | e <mod-spec> ::=
0654:             * "add:" <mod-val> | "delete:" <mod-val> | "replace:" <mod-val> <mod-val>
0655:             * ::= FILL ATTRIBUTE-DESCRIPTION SEP ATTRVAL-SPEC <attrval-specs-e> "-" SEP
0656:             * <attrval-specs-e> ::= ATTRVAL-SPEC <attrval-specs> | e
0657:             *
0658:             * @param entry     The entry to feed
0659:             * @param iter      The lines iterator
0660:             * @param operation The change operation (add, modify, delete, moddn or modrdn)
0661:             * @param control   The associated control, if any
0662:             */
0663:            private void parseChange(Entry entry, Iterator iter, int operation,
0664:                    Control control) {
0665:                // The changetype and operation has already been parsed.
0666:                entry.setChangeType(operation);
0667:
0668:                switch (operation) {
0669:                case Entry.DELETE:
0670:                    // The change type will tell that it's a delete operation,
0671:                    // the dn is used as a key.
0672:                    return;
0673:
0674:                case Entry.ADD:
0675:                    // We will iterate through all attribute/value pairs
0676:                    while (iter.hasNext()) {
0677:                        String line = (String) iter.next();
0678:                        parseAttributeValue(entry, line);
0679:                    }
0680:
0681:                    return;
0682:
0683:                case Entry.MODIFY:
0684:                    parseModify(entry, iter);
0685:                    return;
0686:
0687:                case Entry.MODRDN:// They are supposed to have the same syntax ???
0688:                case Entry.MODDN:
0689:                    // First, parse the modrdn part
0690:                    parseModRdn(entry, iter);
0691:
0692:                    // The next line should be the new superior
0693:                    if (iter.hasNext()) {
0694:                        String line = (String) iter.next();
0695:                        String lowerLine = line.toLowerCase();
0696:
0697:                        if (lowerLine.startsWith("newsuperior:")) {
0698:                            int colonIndex = line.indexOf(':');
0699:                            Object attributeValue = parseValue(line, colonIndex);
0700:                            entry
0701:                                    .setNewSuperior(attributeValue instanceof  String ? (String) attributeValue
0702:                                            : Utils
0703:                                                    .utf8ToString((byte[]) attributeValue));
0704:                        } else {
0705:                            if (operation == Entry.MODDN) {
0706:                                throw new LdifParseException(
0707:                                        "Bad moddn operation, no newsuperior",
0708:                                        line);
0709:                            }
0710:                        }
0711:                    } else {
0712:                        if (operation == Entry.MODDN) {
0713:                            throw new LdifParseException(
0714:                                    "Bad moddn operation, no newsuperior");
0715:                        }
0716:                    }
0717:
0718:                    return;
0719:
0720:                default:
0721:                    // This is an error
0722:                    throw new LdifParseException("Bad operation");
0723:                }
0724:            }
0725:
0726:            /**
0727:             * Parse a ldif file. The following rules are processed :
0728:             * <p/>
0729:             * <ldif-file> ::= <ldif-attrval-record> <ldif-attrval-records> |
0730:             * <ldif-change-record> <ldif-change-records> <ldif-attrval-record> ::=
0731:             * <dn-spec> <sep> <attrval-spec> <attrval-specs> <ldif-change-record> ::=
0732:             * <dn-spec> <sep> <controls-e> <changerecord> <dn-spec> ::= "dn:" <fill>
0733:             * <distinguishedName> | "dn::" <fill> <base64-distinguishedName>
0734:             * <changerecord> ::= "changetype:" <fill> <change-op>
0735:             */
0736:            private Entry parseEntry() {
0737:                if ((lines == null) || (lines.size() == 0)) {
0738:                    return null;
0739:                }
0740:
0741:                // The entry must start with a dn: or a dn::
0742:                String line = lines.get(0);
0743:
0744:                String dn = parseDn(line);
0745:
0746:                // Ok, we have found a DN
0747:                Entry entry = new Entry();
0748:                entry.setDn(dn);
0749:
0750:                // We remove this dn from the lines
0751:                lines.remove(0);
0752:
0753:                // Now, let's iterate through the other lines
0754:                Iterator iter = lines.iterator();
0755:
0756:                // This flag is used to distinguish between an entry and a change
0757:                int type = UNKNOWN;
0758:
0759:                // The following boolean is used to check that a control is *not*
0760:                // found elswhere than just after the dn
0761:                boolean controlSeen = false;
0762:
0763:                // We use this boolean to check that we do not have AttributeValues
0764:                // after a change operation
0765:                boolean changeTypeSeen = false;
0766:
0767:                int operation = Entry.ADD;
0768:                String lowerLine = null;
0769:                Control control = null;
0770:
0771:                while (iter.hasNext()) {
0772:                    // Each line could start either with an OID, an attribute type, with
0773:                    // "control:" or with "changetype:"
0774:                    line = (String) iter.next();
0775:                    lowerLine = line.toLowerCase();
0776:
0777:                    // We have three cases :
0778:                    // 1) The first line after the DN is a "control:"
0779:                    // 2) The first line after the DN is a "changeType:"
0780:                    // 3) The first line after the DN is anything else
0781:                    if (lowerLine.startsWith("control:")) {
0782:                        if (containsEntries) {
0783:                            throw new LdifParseException(
0784:                                    "No changes withing entries", line);
0785:                        }
0786:
0787:                        containsChanges = true;
0788:
0789:                        if (controlSeen) {
0790:                            throw new LdifParseException("Control misplaced",
0791:                                    line);
0792:                        }
0793:
0794:                        // Parse the control
0795:                        control = parseControl(line.substring("control:"
0796:                                .length()));
0797:                        entry.setControl(control);
0798:
0799:                    } else if (lowerLine.startsWith("changetype:")) {
0800:                        if (containsEntries) {
0801:                            throw new LdifParseException(
0802:                                    "No changes withing entries", line);
0803:                        }
0804:
0805:                        containsChanges = true;
0806:
0807:                        if (changeTypeSeen) {
0808:                            throw new LdifParseException(
0809:                                    "ChangeType misplaced", line);
0810:                        }
0811:
0812:                        // A change request
0813:                        type = CHANGE;
0814:                        controlSeen = true;
0815:
0816:                        operation = parseChangeType(line);
0817:
0818:                        // Parse the change operation in a separate function
0819:                        parseChange(entry, iter, operation, control);
0820:                        changeTypeSeen = true;
0821:                    } else if (line.indexOf(':') > 0) {
0822:                        if (containsChanges) {
0823:                            throw new LdifParseException(
0824:                                    "No entries within changes", line);
0825:                        }
0826:
0827:                        containsEntries = true;
0828:
0829:                        if (controlSeen || changeTypeSeen) {
0830:                            throw new LdifParseException(
0831:                                    "AttributeType misplaced", line);
0832:                        }
0833:
0834:                        parseAttributeValue(entry, line);
0835:                        type = ENTRY;
0836:                    } else {
0837:                        // Invalid attribute Value
0838:                        throw new LdifParseException("Bad attribute", line);
0839:                    }
0840:                }
0841:
0842:                if (type == CHANGE) {
0843:                    entry.setChangeType(operation);
0844:                }
0845:
0846:                return entry;
0847:            }
0848:
0849:            /**
0850:             * "version:" <fill> <number>
0851:             */
0852:            static final Pattern VERSION_PATTERN = Pattern
0853:                    .compile("[ ]*version\\:[ ]*(\\d+)[ ]*");
0854:
0855:            /**
0856:             * "version:" <fill>
0857:             *  <number>
0858:             */
0859:
0860:            static final Pattern VERSION_PATTERN_LINE1 = Pattern
0861:                    .compile("[ ]*version\\:[ ]*");
0862:            static final Pattern VERSION_PATTERN_LINE2 = Pattern
0863:                    .compile("[ ]\\d+");
0864:
0865:            /**
0866:             * Parse the version from the ldif input.
0867:             *
0868:             * @return A number representing the version (default to 1)
0869:             */
0870:            private int parseVersion() {
0871:
0872:                // First, read a list of lines
0873:                readLines();
0874:
0875:                if (lines.size() == 0) {
0876:                    return DEFAULT_VERSION;
0877:                }
0878:
0879:                // get the first line
0880:                String line = lines.get(0);
0881:
0882:                Matcher versionMatcher = VERSION_PATTERN.matcher(line);
0883:                String versionStr = null;
0884:
0885:                if (versionMatcher.matches()) {
0886:                    versionStr = versionMatcher.group(1);
0887:                    // We have found the version, just discard the line from the list
0888:                    lines.remove(0);
0889:                } else {
0890:                    versionMatcher = VERSION_PATTERN_LINE1.matcher(line);
0891:                    if (versionMatcher.matches()) {
0892:                        lines.remove(0);
0893:                        if (!lines.isEmpty()) {
0894:                            versionMatcher = VERSION_PATTERN_LINE2
0895:                                    .matcher(lines.get(1));
0896:                            if (versionMatcher.matches()) {
0897:                                versionStr = versionMatcher.group(1);
0898:                            }
0899:                            lines.remove(0);
0900:                        }
0901:
0902:                    }
0903:
0904:                }
0905:
0906:                if (versionStr != null) {
0907:                    try {
0908:                        return Integer.parseInt(versionStr.trim());
0909:                    } catch (NumberFormatException e) {
0910:                        throw new LdifParseException(
0911:                                "Invalid LDIF version number " + versionStr,
0912:                                line);
0913:                    }
0914:                } else {
0915:                    return DEFAULT_VERSION;
0916:                }
0917:            }
0918:
0919:            /**
0920:             * Reads an entry in a ldif buffer, and returns the resulting lines, without
0921:             * comments, and unfolded.
0922:             * <p/>
0923:             * The lines represent *one* entry.
0924:             *
0925:             */
0926:            private void readLines() {
0927:                String line;
0928:                boolean insideComment = true;
0929:                boolean isFirstLine = true;
0930:
0931:                lines.clear();
0932:                StringBuilder sb = new StringBuilder(128);
0933:
0934:                while (in.hasNext()) { //while not EOF
0935:                    line = in.next();
0936:                    if (StringUtils.isAsciiWhitespacesOnly(line)) { //if line is empty
0937:                        if (isFirstLine) {
0938:                            continue;
0939:                        } else {
0940:                            // The line is empty, we have read an entry
0941:                            insideComment = false;
0942:                            if (lines.isEmpty()) { //if block is empty, i.e. comments section - read the next entry
0943:                                continue;
0944:                            } else { //otherwise stop
0945:                                break;
0946:                            }
0947:                        }
0948:                    }
0949:
0950:                    isFirstLine = false;
0951:
0952:                    // We will read the first line which is not a comment
0953:                    switch (line.charAt(0)) {
0954:                    case '#':
0955:                        insideComment = true;
0956:                        break;
0957:
0958:                    case ' ':
0959:                        if (insideComment) {
0960:                            continue;
0961:                        } else if (sb.length() == 0) {
0962:                            throw new LdifParseException(
0963:                                    "Ldif Parsing error: Cannot have an empty continuation line");
0964:                        } else {
0965:                            sb.append(line.substring(1));
0966:                        }
0967:
0968:                        insideComment = false;
0969:                        break;
0970:
0971:                    default:
0972:                        // We have found a new entry
0973:                        // First, stores the previous one if any.
0974:                        if (sb.length() != 0) {
0975:                            lines.add(sb.toString());
0976:                        }
0977:
0978:                        sb = new StringBuilder(line);
0979:                        insideComment = false;
0980:                        break;
0981:                    }
0982:                }
0983:
0984:                // Stores the current line if necessary.
0985:                if (sb.length() != 0) {
0986:                    lines.add(sb.toString());
0987:                }
0988:
0989:            }
0990:
0991:            // ------------------------------------------------------------------------
0992:            // Iterator Methods
0993:            // ------------------------------------------------------------------------
0994:
0995:            /**
0996:             * Gets the next LDIF on the channel.
0997:             *
0998:             * @return the next LDIF as a String.
0999:             */
1000:            public Entry next() {
1001:                if (!hasNext()) {
1002:                    throw new NoSuchElementException(
1003:                            "No LDIF entries to read. Use hasNext().");
1004:                }
1005:                Entry res = prefetched;
1006:                prefetched = null;
1007:                return res;
1008:            }
1009:
1010:            /**
1011:             * Tests to see if another LDIF is on the input channel.
1012:             *
1013:             * @return true if another LDIF is available false otherwise.
1014:             */
1015:            public boolean hasNext() {
1016:                if (prefetched == null) {
1017:                    readLines();
1018:                    prefetched = parseEntry();
1019:                }
1020:                return null != prefetched;
1021:            }
1022:
1023:            /**
1024:             * Always throws UnsupportedOperationException!
1025:             *
1026:             * @see java.util.Iterator#remove()
1027:             */
1028:            public void remove() {
1029:                throw new UnsupportedOperationException();
1030:            }
1031:
1032:            /**
1033:             * @return An iterator on the file
1034:             */
1035:            public Iterator<Entry> iterator() {
1036:                return this ;
1037:            }
1038:
1039:            /**
1040:             * Just a helper method
1041:             */
1042:            List<Entry> asList() {
1043:                // Create a list that will contain the read entries
1044:                List<Entry> entries = new ArrayList<Entry>();
1045:
1046:                // When done, get the entries one by one.
1047:                while (hasNext()) {
1048:                    Entry entry = next();
1049:                    entries.add(entry);
1050:                }
1051:                return entries;
1052:            }
1053:
1054:            /**
1055:             * @return True if the ldif file contains entries, fals if it contains
1056:             *         changes
1057:             */
1058:            public boolean containsEntries() {
1059:                return containsEntries;
1060:            }
1061:
1062:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.