001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.xbean.osgi.bundle.util;
020
021import java.util.ArrayList;
022import java.util.HashMap;
023import java.util.List;
024import java.util.Map;
025
026/**
027 * Utility class to parse standard OSGi headers.
028 *
029 * @version $Rev: 1167021 $, $Date: 2011-09-09 09:01:22 +0200 (Fri, 09 Sep 2011) $
030 */
031public class HeaderParser  {
032
033    /**
034     * Parse a given OSGi header into a list of header elements.
035     *
036     * @param header the OSGi header to parse
037     * @return the list of header elements extracted from this header
038     */
039    public static List<HeaderElement> parseHeader(String header) {
040        List<HeaderElement> elements = new ArrayList<HeaderElement>();
041        if (header == null || header.trim().length() == 0) {
042            return elements;
043        }
044        List<String> clauses = parseDelimitedString(header, ",", false);
045        for (String clause : clauses) {
046            String[] tokens = clause.split(";");
047            if (tokens.length < 1) {
048                throw new IllegalArgumentException("Invalid header clause: " + clause);
049            }
050            HeaderElement elem = new HeaderElement(tokens[0].trim());
051            elements.add(elem);
052            int beginIndex = elements.size() - 1;
053            for (int i = 1; i < tokens.length; i++) {
054                int pos = tokens[i].indexOf('=');
055                if (pos != -1) {
056                    if (pos > 0 && tokens[i].charAt(pos - 1) == ':') {
057                        String name = tokens[i].substring(0, pos - 1).trim();
058                        String value = tokens[i].substring(pos + 1).trim();
059                        elem.addDirective(name, value);
060                    } else {
061                        String name = tokens[i].substring(0, pos).trim();
062                        String value = tokens[i].substring(pos + 1).trim();
063                        elem.addAttribute(name, value);
064                    }
065                } else {
066                    elem = new HeaderElement(tokens[i].trim());
067                    elements.add(elem);
068                }
069            }
070            for (; beginIndex < elements.size() - 1; beginIndex++) {
071                HeaderElement headerElement = elements.get(beginIndex);
072                headerElement.getAttributes().putAll(elem.getAttributes());
073                headerElement.getDirectives().putAll(elem.getDirectives());
074            }
075        }
076        return elements;
077    }
078
079    private static List<String> parseDelimitedString(String value, String delim, boolean includeQuotes) {
080        if (value == null) {
081            value = "";
082        }
083
084        List<String> list = new ArrayList<String>();
085
086        int CHAR = 1;
087        int DELIMITER = 2;
088        int STARTQUOTE = 4;
089        int ENDQUOTE = 8;
090
091        StringBuffer sb = new StringBuffer();
092
093        int expecting = (CHAR | DELIMITER | STARTQUOTE);
094
095        for (int i = 0; i < value.length(); i++) {
096            char c = value.charAt(i);
097
098            boolean isDelimiter = (delim.indexOf(c) >= 0);
099            boolean isQuote = (c == '"');
100
101            if (isDelimiter && ((expecting & DELIMITER) > 0)) {
102                list.add(sb.toString().trim());
103                sb.delete(0, sb.length());
104                expecting = (CHAR | DELIMITER | STARTQUOTE);
105            } else if (isQuote && ((expecting & STARTQUOTE) > 0)) {
106                if (includeQuotes) {
107                    sb.append(c);
108                }
109                expecting = CHAR | ENDQUOTE;
110            } else if (isQuote && ((expecting & ENDQUOTE) > 0)) {
111                if (includeQuotes) {
112                    sb.append(c);
113                }
114                expecting = (CHAR | STARTQUOTE | DELIMITER);
115            } else if ((expecting & CHAR) > 0) {
116                sb.append(c);
117            } else {
118                throw new IllegalArgumentException("Invalid delimited string: " + value);
119            }
120        }
121
122        if (sb.length() > 0) {
123            list.add(sb.toString().trim());
124        }
125
126        return list;
127    }
128
129    public static class HeaderElement {
130
131        private String path;
132        private Map<String, String> attributes;
133        private Map<String, String> directives;
134
135        public HeaderElement(String path) {
136            this.path = path;
137            this.attributes = new HashMap<String, String>();
138            this.directives = new HashMap<String, String>();
139        }
140
141        public String getName() {
142            return this.path;
143        }
144
145        public Map<String, String> getAttributes() {
146            return attributes;
147        }
148
149        public String getAttribute(String name) {
150            return attributes.get(name);
151        }
152
153        public void addAttribute(String name, String value) {
154            attributes.put(name, value);
155        }
156
157        public Map<String, String> getDirectives() {
158            return directives;
159        }
160
161        public String getDirective(String name) {
162            return directives.get(name);
163        }
164
165        public void addDirective(String name, String value) {
166            directives.put(name, value);
167        }
168
169        @Override
170        public String toString() {
171            return "HeaderElement [path=" + path + ", attributes=" + attributes + ", directives=" + directives + "]";
172        }
173
174        @Override
175        public int hashCode() {
176            final int prime = 31;
177            int result = 1;
178            result = prime * result + ((attributes == null) ? 0 : attributes.hashCode());
179            result = prime * result + ((directives == null) ? 0 : directives.hashCode());
180            result = prime * result + ((path == null) ? 0 : path.hashCode());
181            return result;
182        }
183
184        @Override
185        public boolean equals(Object obj) {
186            if (this == obj)
187                return true;
188            if (obj == null)
189                return false;
190            if (getClass() != obj.getClass())
191                return false;
192            HeaderElement other = (HeaderElement) obj;
193            if (attributes == null) {
194                if (other.attributes != null)
195                    return false;
196            } else if (!attributes.equals(other.attributes))
197                return false;
198            if (directives == null) {
199                if (other.directives != null)
200                    return false;
201            } else if (!directives.equals(other.directives))
202                return false;
203            if (path == null) {
204                if (other.path != null)
205                    return false;
206            } else if (!path.equals(other.path))
207                return false;
208            return true;
209        }
210    }
211}