Author: cziegeler
Date: Thu May 15 00:10:03 2008
New Revision: 656526
URL: http://svn.apache.org/viewvc?rev=656526&view=rev
Log:
SLING-456: Add manifest header parser and junit test.
Added:
incubator/sling/trunk/commons/osgi/src/main/java/org/apache/sling/commons/osgi/ManifestHeader.java
(with props)
incubator/sling/trunk/commons/osgi/src/test/
incubator/sling/trunk/commons/osgi/src/test/java/
incubator/sling/trunk/commons/osgi/src/test/java/org/
incubator/sling/trunk/commons/osgi/src/test/java/org/apache/
incubator/sling/trunk/commons/osgi/src/test/java/org/apache/sling/
incubator/sling/trunk/commons/osgi/src/test/java/org/apache/sling/commons/
incubator/sling/trunk/commons/osgi/src/test/java/org/apache/sling/commons/osgi/
incubator/sling/trunk/commons/osgi/src/test/java/org/apache/sling/commons/osgi/ManifestHeaderTest.java
(with props)
Added:
incubator/sling/trunk/commons/osgi/src/main/java/org/apache/sling/commons/osgi/ManifestHeader.java
URL:
http://svn.apache.org/viewvc/incubator/sling/trunk/commons/osgi/src/main/java/org/apache/sling/commons/osgi/ManifestHeader.java?rev=656526&view=auto
==============================================================================
---
incubator/sling/trunk/commons/osgi/src/main/java/org/apache/sling/commons/osgi/ManifestHeader.java
(added)
+++
incubator/sling/trunk/commons/osgi/src/main/java/org/apache/sling/commons/osgi/ManifestHeader.java
Thu May 15 00:10:03 2008
@@ -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;
+ }
+
+ }
+}
Propchange:
incubator/sling/trunk/commons/osgi/src/main/java/org/apache/sling/commons/osgi/ManifestHeader.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
incubator/sling/trunk/commons/osgi/src/main/java/org/apache/sling/commons/osgi/ManifestHeader.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Added:
incubator/sling/trunk/commons/osgi/src/test/java/org/apache/sling/commons/osgi/ManifestHeaderTest.java
URL:
http://svn.apache.org/viewvc/incubator/sling/trunk/commons/osgi/src/test/java/org/apache/sling/commons/osgi/ManifestHeaderTest.java?rev=656526&view=auto
==============================================================================
---
incubator/sling/trunk/commons/osgi/src/test/java/org/apache/sling/commons/osgi/ManifestHeaderTest.java
(added)
+++
incubator/sling/trunk/commons/osgi/src/test/java/org/apache/sling/commons/osgi/ManifestHeaderTest.java
Thu May 15 00:10:03 2008
@@ -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());
+ }
+}
Propchange:
incubator/sling/trunk/commons/osgi/src/test/java/org/apache/sling/commons/osgi/ManifestHeaderTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
incubator/sling/trunk/commons/osgi/src/test/java/org/apache/sling/commons/osgi/ManifestHeaderTest.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url