Save This Page
Home » org.apache.sling.commons.osgi-2.0.6-source-release » org.apache.sling.commons.osgi » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
   10    *   http://www.apache.org/licenses/LICENSE-2.0
   11    *
   12    * Unless required by applicable law or agreed to in writing,
   13    * software distributed under the License is distributed on an
   14    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15    * KIND, either express or implied.  See the License for the
   16    * specific language governing permissions and limitations
   17    * under the License.
   18    */
   19   package org.apache.sling.commons.osgi;
   20   
   21   import java.util.ArrayList;
   22   import java.util.HashSet;
   23   import java.util.List;
   24   import java.util.Set;
   25   
   26   /**
   27    * This is a helper class to parse manifest header entries.
   28    */
   29   public class ManifestHeader {
   30   
   31       /**
   32        * A header can have several entries separated by comma.
   33        */
   34       public interface Entry {
   35   
   36           /**
   37            * The value of the entry.
   38            */
   39           String getValue();
   40   
   41           /**
   42            * The attributes specified for this entry.
   43            */
   44           NameValuePair[] getAttributes();
   45   
   46           /**
   47            * The directives for this entry.
   48            */
   49           NameValuePair[] getDirectives();
   50   
   51           String getAttributeValue(String name);
   52   
   53           String getDirectiveValue(String name);
   54       }
   55   
   56       /** The entries for this header. */
   57       private Entry[] entries = new Entry[0];
   58   
   59       /**
   60        * Add new entries from parsing.
   61        */
   62       private void add(Entry[] paths) {
   63           if ( paths != null && paths.length > 0 ) {
   64               final Entry[] copy = new Entry[this.entries.length + paths.length];
   65               System.arraycopy(this.entries, 0, copy, 0, this.entries.length);
   66               System.arraycopy(paths, 0, copy, this.entries.length, paths.length);
   67               this.entries = copy;
   68           }
   69       }
   70   
   71       /**
   72        * Return the entries for this header.
   73        */
   74       public Entry[] getEntries() {
   75           return this.entries;
   76       }
   77   
   78       /**
   79        * Directives and attributes are simple name/value pairs.
   80        */
   81       public final static class NameValuePair {
   82   
   83           private final String name;
   84           private final String value;
   85   
   86           public NameValuePair(String name, String value) {
   87               this.name = name;
   88               this.value = value;
   89           }
   90   
   91           public String getName() {
   92               return name;
   93           }
   94   
   95           public String getValue() {
   96               return value;
   97           }
   98       }
   99   
  100       private static final String CLASS_PATH_SEPARATOR = ",";
  101       private static final String PACKAGE_SEPARATOR = ";";
  102       private static final String DIRECTIVE_SEPARATOR = ":=";
  103       private static final String ATTRIBUTE_SEPARATOR = "=";
  104   
  105       /**
  106        * Parse headers
  107        * Like this: path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2,
  108        *            path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2
  109        * The returned object maintains the order of entries (paths), directives and attributes.
  110        */
  111       public static ManifestHeader parse(String header) {
  112           final ManifestHeader entry = new ManifestHeader();
  113   
  114           if (header != null) {
  115               if (header.length() == 0) {
  116                   throw new IllegalArgumentException("A header cannot be an empty string.");
  117               }
  118   
  119               final String[] clauseStrings = parseDelimitedString(header, CLASS_PATH_SEPARATOR);
  120               if ( clauseStrings != null ) {
  121                   for(final String clause : clauseStrings) {
  122                       entry.add(parseStandardHeaderClause(clause));
  123                   }
  124               }
  125           }
  126   
  127           return (entry.getEntries().length == 0) ? null : entry;
  128       }
  129   
  130       /**
  131        * Parse a clause
  132        * Like this: path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2
  133        */
  134       private static ManifestHeader.Entry[] parseStandardHeaderClause(String clauseString)
  135       throws IllegalArgumentException {
  136           // Break string into semi-colon delimited pieces.
  137           String[] pieces = parseDelimitedString(clauseString, PACKAGE_SEPARATOR);
  138   
  139           // Count the number of different paths; paths
  140           // will not have an '=' in their string. This assumes
  141           // that paths come first, before directives and
  142           // attributes.
  143           int pathCount = 0;
  144           for (int pieceIdx = 0; pieceIdx < pieces.length; pieceIdx++) {
  145               if (pieces[pieceIdx].indexOf('=') >= 0) {
  146                   break;
  147               }
  148               pathCount++;
  149           }
  150   
  151           // Error if no paths were specified.
  152           if (pathCount == 0) {
  153               throw new IllegalArgumentException(
  154                   "No paths specified in header: " + clauseString);
  155           }
  156   
  157           // Create an array of paths.
  158           PathImpl[] paths = new PathImpl[pathCount];
  159           for(int i=0;i<pathCount;i++) {
  160               paths[i] = new PathImpl(pieces[i]);
  161           }
  162   
  163           // Parse the directives/attributes
  164           // and keep the order
  165           // for simpliefied checking if a directive/attribute is used twice, we keep
  166           // two collections: one for the values and one for the names
  167           final List<ManifestHeader.NameValuePair> dirsList = new ArrayList<ManifestHeader.NameValuePair>();
  168           final Set<String> dirsNames = new HashSet<String>();
  169           final List<ManifestHeader.NameValuePair> attrsList = new ArrayList<ManifestHeader.NameValuePair>();
  170           final Set<String> attrsNames = new HashSet<String>();
  171   
  172           int idx = -1;
  173           String sep = null;
  174           for (int pieceIdx = pathCount; pieceIdx < pieces.length; pieceIdx++) {
  175   
  176               if ((idx = pieces[pieceIdx].indexOf(DIRECTIVE_SEPARATOR)) >= 0) {
  177                   sep = DIRECTIVE_SEPARATOR;
  178               } else if ((idx = pieces[pieceIdx].indexOf(ATTRIBUTE_SEPARATOR)) >= 0) {
  179                   sep = ATTRIBUTE_SEPARATOR;
  180               } else {
  181                   throw new IllegalArgumentException("Not a directive/attribute: " + clauseString);
  182               }
  183   
  184               final String key = pieces[pieceIdx].substring(0, idx).trim();
  185               String value = pieces[pieceIdx].substring(idx + sep.length()).trim();
  186   
  187               // Remove quotes, if value is quoted.
  188               if (value.startsWith("\"") && value.endsWith("\"")) {
  189                   value = value.substring(1, value.length() - 1);
  190               }
  191   
  192               // Save the directive/attribute in the appropriate array.
  193               if (sep.equals(DIRECTIVE_SEPARATOR)) {
  194                   // Check for duplicates.
  195                   if (dirsNames.contains(key)) {
  196                       throw new IllegalArgumentException("Duplicate directive: " + key);
  197                   }
  198                   dirsList.add(new ManifestHeader.NameValuePair(key, value));
  199                   dirsNames.add(key);
  200               } else {
  201                   // Check for duplicates.
  202                   if (attrsNames.contains(key)) {
  203                       throw new IllegalArgumentException("Duplicate attribute: " + key);
  204                   }
  205                   attrsList.add(new ManifestHeader.NameValuePair(key, value));
  206                   attrsNames.add(key);
  207               }
  208           }
  209           // Create directive array.
  210           ManifestHeader.NameValuePair[] dirs =
  211               dirsList.toArray(new ManifestHeader.NameValuePair[dirsList.size()]);
  212   
  213           // Create attribute array.
  214           ManifestHeader.NameValuePair[] attrs =
  215               attrsList.toArray(new ManifestHeader.NameValuePair[attrsList.size()]);
  216   
  217           // now set attributes and directives for each path
  218           for(int i=0;i<pathCount;i++) {
  219               paths[i].init(dirs, attrs);
  220           }
  221   
  222           return paths;
  223       }
  224   
  225       private static final int CHAR = 1;
  226       private static final int DELIMITER = 2;
  227       private static final int STARTQUOTE = 4;
  228       private static final int ENDQUOTE = 8;
  229   
  230       /**
  231        * Parses delimited string and returns an array containing the tokens. This
  232        * parser obeys quotes, so the delimiter character will be ignored if it is
  233        * inside of a quote. This method assumes that the quote character is not
  234        * included in the set of delimiter characters.
  235        * @param value the delimited string to parse.
  236        * @param delim the characters delimiting the tokens.
  237        * @return an array of string tokens or null if there were no tokens.
  238        **/
  239       private static String[] parseDelimitedString(String value, String delim) {
  240           if (value == null) {
  241              value = "";
  242           }
  243   
  244           final List<String> list = new ArrayList<String>();
  245   
  246           final StringBuffer sb = new StringBuffer();
  247   
  248           int expecting = (CHAR | DELIMITER | STARTQUOTE);
  249   
  250           for (int i = 0; i < value.length(); i++) {
  251               char c = value.charAt(i);
  252   
  253               boolean isDelimiter = (delim.indexOf(c) >= 0);
  254               boolean isQuote = (c == '"');
  255   
  256               if (isDelimiter && ((expecting & DELIMITER) > 0)) {
  257                   list.add(sb.toString().trim());
  258                   sb.delete(0, sb.length());
  259                   expecting = (CHAR | DELIMITER | STARTQUOTE);
  260               } else if (isQuote && ((expecting & STARTQUOTE) > 0)) {
  261                   sb.append(c);
  262                   expecting = CHAR | ENDQUOTE;
  263               } else if (isQuote && ((expecting & ENDQUOTE) > 0)) {
  264                   sb.append(c);
  265                   expecting = (CHAR | STARTQUOTE | DELIMITER);
  266               } else if ((expecting & CHAR) > 0) {
  267                   sb.append(c);
  268               } else {
  269                   throw new IllegalArgumentException("Invalid delimited string: " + value);
  270               }
  271           }
  272   
  273           if (sb.length() > 0) {
  274               list.add(sb.toString().trim());
  275           }
  276   
  277           if ( list.size() == 0 ) {
  278               return null;
  279           }
  280           return list.toArray(new String[list.size()]);
  281       }
  282   
  283       protected static final class PathImpl implements ManifestHeader.Entry {
  284   
  285           private final String value;
  286   
  287           private NameValuePair[] attributes;
  288           private NameValuePair[] directives;
  289   
  290           public PathImpl(final String path) {
  291               this.value = path;
  292           }
  293   
  294           public void init(NameValuePair[] dirs, NameValuePair[] attrs) {
  295               this.directives = dirs;
  296               this.attributes = attrs;
  297           }
  298   
  299           /**
  300            * @see org.apache.sling.commons.osgi.ManifestHeader.Entry#getAttributes()
  301            */
  302           public NameValuePair[] getAttributes() {
  303               return this.attributes;
  304           }
  305   
  306           /**
  307            * @see org.apache.sling.commons.osgi.ManifestHeader.Entry#getDirectives()
  308            */
  309           public NameValuePair[] getDirectives() {
  310               return this.directives;
  311           }
  312   
  313           /**
  314            * @see org.apache.sling.commons.osgi.ManifestHeader.Entry#getValue()
  315            */
  316           public String getValue() {
  317               return this.value;
  318           }
  319   
  320           public String getAttributeValue(String name) {
  321               String v = null;
  322               int index = 0;
  323               while ( v == null && index < attributes.length ) {
  324                   if ( attributes[index].getName().equals(name) ) {
  325                       v = attributes[index].getValue();
  326                   }
  327                   index++;
  328               }
  329               return v;
  330           }
  331   
  332           public String getDirectiveValue(String name) {
  333               String v = null;
  334               int index = 0;
  335               while ( v == null && index < directives.length ) {
  336                   if ( directives[index].getName().equals(name) ) {
  337                       v = directives[index].getValue();
  338                   }
  339                   index++;
  340               }
  341               return v;
  342           }
  343       }
  344   }

Save This Page
Home » org.apache.sling.commons.osgi-2.0.6-source-release » org.apache.sling.commons.osgi » [javadoc | source]