This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.commons.osgi-2.0.2-incubator in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-osgi.git
commit 0665281fc87719ec715317a35948e807dccf2704 Author: Carsten Ziegeler <[email protected]> AuthorDate: Thu May 15 07:10:03 2008 +0000 SLING-456: Add manifest header parser and junit test. git-svn-id: https://svn.apache.org/repos/asf/incubator/sling/trunk/commons/osgi@656526 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/sling/commons/osgi/ManifestHeader.java | 309 +++++++++++++++++++++ .../sling/commons/osgi/ManifestHeaderTest.java | 89 ++++++ 2 files changed, 398 insertions(+) diff --git a/src/main/java/org/apache/sling/commons/osgi/ManifestHeader.java b/src/main/java/org/apache/sling/commons/osgi/ManifestHeader.java new file mode 100644 index 0000000..f7a7e85 --- /dev/null +++ b/src/main/java/org/apache/sling/commons/osgi/ManifestHeader.java @@ -0,0 +1,309 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.sling.commons.osgi; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This is a helper class to parse manifest header entries. + */ +public class ManifestHeader { + + /** + * A header can have several entries separated by comma. + */ + public interface Entry { + + /** + * The value of the entry. + */ + String getValue(); + + /** + * The attributes specified for this entry. + */ + NameValuePair[] getAttributes(); + + /** + * The directives for this entry. + */ + NameValuePair[] getDirectives(); + } + + /** The entries for this header. */ + private Entry[] entries = new Entry[0]; + + /** + * Add new entries from parsing. + */ + private void add(Entry[] paths) { + if ( paths != null && paths.length > 0 ) { + final Entry[] copy = new Entry[this.entries.length + paths.length]; + System.arraycopy(this.entries, 0, copy, 0, this.entries.length); + System.arraycopy(paths, 0, copy, this.entries.length, paths.length); + this.entries = copy; + } + } + + /** + * Return the entries for this header. + */ + public Entry[] getEntries() { + return this.entries; + } + + /** + * Directives and attributes are simple name/value pairs. + */ + public final static class NameValuePair { + + private final String name; + private final String value; + + public NameValuePair(String name, String value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public String getValue() { + return value; + } + } + + private static final String CLASS_PATH_SEPARATOR = ","; + private static final String PACKAGE_SEPARATOR = ";"; + private static final String DIRECTIVE_SEPARATOR = ":="; + private static final String ATTRIBUTE_SEPARATOR = "="; + + /** + * Parse headers + * Like this: path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2, + * path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2 + */ + public static ManifestHeader parse(String header) { + final ManifestHeader entry = new ManifestHeader(); + + if (header != null) { + if (header.length() == 0) { + throw new IllegalArgumentException("A header cannot be an empty string."); + } + + final String[] clauseStrings = parseDelimitedString(header, CLASS_PATH_SEPARATOR); + if ( clauseStrings != null ) { + for(final String clause : clauseStrings) { + entry.add(parseStandardHeaderClause(clause)); + } + } + } + + return (entry.getEntries().length == 0) ? null : entry; + } + + /** + * Parse a clause + * Like this: path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2 + */ + private static ManifestHeader.Entry[] parseStandardHeaderClause(String clauseString) + throws IllegalArgumentException { + // Break string into semi-colon delimited pieces. + String[] pieces = parseDelimitedString(clauseString, PACKAGE_SEPARATOR); + + // Count the number of different paths; paths + // will not have an '=' in their string. This assumes + // that paths come first, before directives and + // attributes. + int pathCount = 0; + for (int pieceIdx = 0; pieceIdx < pieces.length; pieceIdx++) { + if (pieces[pieceIdx].indexOf('=') >= 0) { + break; + } + pathCount++; + } + + // Error if no paths were specified. + if (pathCount == 0) { + throw new IllegalArgumentException( + "No paths specified in header: " + clauseString); + } + + // Create an array of paths. + PathImpl[] paths = new PathImpl[pathCount]; + for(int i=0;i<pathCount;i++) { + paths[i] = new PathImpl(pieces[i]); + } + + // Parse the directives/attributes. + final Map<String, ManifestHeader.NameValuePair> dirsMap = new HashMap<String, ManifestHeader.NameValuePair>(); + final Map<String, ManifestHeader.NameValuePair> attrsMap = new HashMap<String, ManifestHeader.NameValuePair>(); + + int idx = -1; + String sep = null; + for (int pieceIdx = pathCount; pieceIdx < pieces.length; pieceIdx++) { + + if ((idx = pieces[pieceIdx].indexOf(DIRECTIVE_SEPARATOR)) >= 0) { + sep = DIRECTIVE_SEPARATOR; + } else if ((idx = pieces[pieceIdx].indexOf(ATTRIBUTE_SEPARATOR)) >= 0) { + sep = ATTRIBUTE_SEPARATOR; + } else { + throw new IllegalArgumentException("Not a directive/attribute: " + clauseString); + } + + final String key = pieces[pieceIdx].substring(0, idx).trim(); + String value = pieces[pieceIdx].substring(idx + sep.length()).trim(); + + // Remove quotes, if value is quoted. + if (value.startsWith("\"") && value.endsWith("\"")) { + value = value.substring(1, value.length() - 1); + } + + // Save the directive/attribute in the appropriate array. + if (sep.equals(DIRECTIVE_SEPARATOR)) { + // Check for duplicates. + if (dirsMap.get(key) != null) { + throw new IllegalArgumentException("Duplicate directive: " + key); + } + dirsMap.put(key, new ManifestHeader.NameValuePair(key, value)); + } else { + // Check for duplicates. + if (attrsMap.get(key) != null) { + throw new IllegalArgumentException("Duplicate attribute: " + key); + } + attrsMap.put(key, new ManifestHeader.NameValuePair(key, value)); + } + } + // Create directive array. + ManifestHeader.NameValuePair[] dirs = + dirsMap.values().toArray(new ManifestHeader.NameValuePair[dirsMap.size()]); + + // Create attribute array. + ManifestHeader.NameValuePair[] attrs = + attrsMap.values().toArray(new ManifestHeader.NameValuePair[attrsMap.size()]); + + // now set attributes and directives for each path + for(int i=0;i<pathCount;i++) { + paths[i].init(dirs, attrs); + } + + return paths; + } + + private static final int CHAR = 1; + private static final int DELIMITER = 2; + private static final int STARTQUOTE = 4; + private static final int ENDQUOTE = 8; + + /** + * Parses delimited string and returns an array containing the tokens. This + * parser obeys quotes, so the delimiter character will be ignored if it is + * inside of a quote. This method assumes that the quote character is not + * included in the set of delimiter characters. + * @param value the delimited string to parse. + * @param delim the characters delimiting the tokens. + * @return an array of string tokens or null if there were no tokens. + **/ + private static String[] parseDelimitedString(String value, String delim) { + if (value == null) { + value = ""; + } + + final List<String> list = new ArrayList<String>(); + + final StringBuffer sb = new StringBuffer(); + + int expecting = (CHAR | DELIMITER | STARTQUOTE); + + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + + boolean isDelimiter = (delim.indexOf(c) >= 0); + boolean isQuote = (c == '"'); + + if (isDelimiter && ((expecting & DELIMITER) > 0)) { + list.add(sb.toString().trim()); + sb.delete(0, sb.length()); + expecting = (CHAR | DELIMITER | STARTQUOTE); + } else if (isQuote && ((expecting & STARTQUOTE) > 0)) { + sb.append(c); + expecting = CHAR | ENDQUOTE; + } else if (isQuote && ((expecting & ENDQUOTE) > 0)) { + sb.append(c); + expecting = (CHAR | STARTQUOTE | DELIMITER); + } else if ((expecting & CHAR) > 0) { + sb.append(c); + } else { + throw new IllegalArgumentException("Invalid delimited string: " + value); + } + } + + if (sb.length() > 0) { + list.add(sb.toString().trim()); + } + + if ( list.size() == 0 ) { + return null; + } + return list.toArray(new String[list.size()]); + } + + protected static final class PathImpl implements ManifestHeader.Entry { + + private final String value; + + private NameValuePair[] attributes; + private NameValuePair[] directives; + + public PathImpl(final String path) { + this.value = path; + } + + public void init(NameValuePair[] dirs, NameValuePair[] attrs) { + this.directives = dirs; + this.attributes = attrs; + } + + /** + * @see org.apache.sling.commons.osgi.ManifestHeader.Entry#getAttributes() + */ + public NameValuePair[] getAttributes() { + return this.attributes; + } + + /** + * @see org.apache.sling.commons.osgi.ManifestHeader.Entry#getDirectives() + */ + public NameValuePair[] getDirectives() { + return this.directives; + } + + /** + * @see org.apache.sling.commons.osgi.ManifestHeader.Entry#getValue() + */ + public String getValue() { + return this.value; + } + + } +} diff --git a/src/test/java/org/apache/sling/commons/osgi/ManifestHeaderTest.java b/src/test/java/org/apache/sling/commons/osgi/ManifestHeaderTest.java new file mode 100644 index 0000000..da43902 --- /dev/null +++ b/src/test/java/org/apache/sling/commons/osgi/ManifestHeaderTest.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.sling.commons.osgi; + +import junit.framework.TestCase; + +/** + * Tests for the manifest header parsing. + */ +public class ManifestHeaderTest extends TestCase { + + public void testNonExisting() { + String header = null; + final ManifestHeader entry = ManifestHeader.parse(header); + assertNull(entry); + } + + public void testSinglePath() { + String header = "something"; + final ManifestHeader entry = ManifestHeader.parse(header); + assertEquals(1, entry.getEntries().length); + assertEquals(header, entry.getEntries()[0].getValue()); + assertEquals(0, entry.getEntries()[0].getAttributes().length); + assertEquals(0, entry.getEntries()[0].getDirectives().length); + } + + public void testSeveralPaths() { + String header = "one,two, three ,\n four, \n five"; + final ManifestHeader entry = ManifestHeader.parse(header); + assertEquals(5, entry.getEntries().length); + assertEquals("one", entry.getEntries()[0].getValue()); + assertEquals(0, entry.getEntries()[0].getAttributes().length); + assertEquals(0, entry.getEntries()[0].getDirectives().length); + assertEquals("two", entry.getEntries()[1].getValue()); + assertEquals(0, entry.getEntries()[1].getAttributes().length); + assertEquals(0, entry.getEntries()[1].getDirectives().length); + assertEquals("three", entry.getEntries()[2].getValue()); + assertEquals(0, entry.getEntries()[2].getAttributes().length); + assertEquals(0, entry.getEntries()[2].getDirectives().length); + assertEquals("four", entry.getEntries()[3].getValue()); + assertEquals(0, entry.getEntries()[3].getAttributes().length); + assertEquals(0, entry.getEntries()[3].getDirectives().length); + assertEquals("five", entry.getEntries()[4].getValue()); + assertEquals(0, entry.getEntries()[4].getAttributes().length); + assertEquals(0, entry.getEntries()[4].getDirectives().length); + } + + public void testAttributes() { + String header = "one;a=1;b=2"; + final ManifestHeader entry = ManifestHeader.parse(header); + assertEquals(1, entry.getEntries().length); + assertEquals("one", entry.getEntries()[0].getValue()); + assertEquals(2, entry.getEntries()[0].getAttributes().length); + assertEquals(0, entry.getEntries()[0].getDirectives().length); + assertEquals("a", entry.getEntries()[0].getAttributes()[0].getName()); + assertEquals("b", entry.getEntries()[0].getAttributes()[1].getName()); + assertEquals("1", entry.getEntries()[0].getAttributes()[0].getValue()); + assertEquals("2", entry.getEntries()[0].getAttributes()[1].getValue()); + } + + public void testDirectives() { + String header = "one;a:=1;b:=2"; + final ManifestHeader entry = ManifestHeader.parse(header); + assertEquals(1, entry.getEntries().length); + assertEquals("one", entry.getEntries()[0].getValue()); + assertEquals(2, entry.getEntries()[0].getDirectives().length); + assertEquals(0, entry.getEntries()[0].getAttributes().length); + assertEquals("a", entry.getEntries()[0].getDirectives()[0].getName()); + assertEquals("b", entry.getEntries()[0].getDirectives()[1].getName()); + assertEquals("1", entry.getEntries()[0].getDirectives()[0].getValue()); + assertEquals("2", entry.getEntries()[0].getDirectives()[1].getValue()); + } +} -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
