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     */
019    package org.apache.xbean.osgi.bundle.util;
020    
021    import java.util.ArrayList;
022    import java.util.HashMap;
023    import java.util.List;
024    import java.util.Map;
025    
026    /**
027     * Utility class to parse standard OSGi headers.
028     *
029     * @version $Rev: 1160977 $, $Date: 2011-08-24 13:02:21 +0800 (Wed, 24 Aug 2011) $
030     */
031    public 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                for (int i = 1; i < tokens.length; i++) {
053                    int pos = tokens[i].indexOf('=');
054                    if (pos != -1) {
055                        if (pos > 0 && tokens[i].charAt(pos - 1) == ':') {
056                            String name = tokens[i].substring(0, pos - 1).trim();
057                            String value = tokens[i].substring(pos + 1).trim();
058                            elem.addDirective(name, value);
059                        } else {
060                            String name = tokens[i].substring(0, pos).trim();
061                            String value = tokens[i].substring(pos + 1).trim();
062                            elem.addAttribute(name, value);
063                        }
064                    } else {
065                        elem = new HeaderElement(tokens[i].trim());
066                        elements.add(elem);
067                    }
068                }
069            }
070            return elements;
071        }
072    
073        private static List<String> parseDelimitedString(String value, String delim, boolean includeQuotes) {   
074            if (value == null) {       
075                value = "";
076            }
077    
078            List<String> list = new ArrayList<String>();
079    
080            int CHAR = 1;
081            int DELIMITER = 2;
082            int STARTQUOTE = 4;
083            int ENDQUOTE = 8;
084    
085            StringBuffer sb = new StringBuffer();
086    
087            int expecting = (CHAR | DELIMITER | STARTQUOTE);
088    
089            for (int i = 0; i < value.length(); i++) {        
090                char c = value.charAt(i);
091    
092                boolean isDelimiter = (delim.indexOf(c) >= 0);
093                boolean isQuote = (c == '"');
094    
095                if (isDelimiter && ((expecting & DELIMITER) > 0)) {            
096                    list.add(sb.toString().trim());
097                    sb.delete(0, sb.length());
098                    expecting = (CHAR | DELIMITER | STARTQUOTE);
099                } else if (isQuote && ((expecting & STARTQUOTE) > 0)) { 
100                    if (includeQuotes) {
101                        sb.append(c);
102                    }
103                    expecting = CHAR | ENDQUOTE;
104                } else if (isQuote && ((expecting & ENDQUOTE) > 0)) {    
105                    if (includeQuotes) {
106                        sb.append(c);
107                    }
108                    expecting = (CHAR | STARTQUOTE | DELIMITER);
109                } else if ((expecting & CHAR) > 0) {            
110                    sb.append(c);
111                } else {
112                    throw new IllegalArgumentException("Invalid delimited string: " + value);
113                }
114            }
115    
116            if (sb.length() > 0) {        
117                list.add(sb.toString().trim());
118            }
119    
120            return list;
121        }
122        
123        public static class HeaderElement {
124            
125            private String path;
126            private Map<String, String> attributes;
127            private Map<String, String> directives;
128            
129            public HeaderElement(String path) {
130                this.path = path;
131                this.attributes = new HashMap<String, String>();
132                this.directives = new HashMap<String, String>();
133            }
134            
135            public String getName() {
136                return this.path;
137            }
138            
139            public Map<String, String> getAttributes() {
140                return attributes;
141            }
142            
143            public String getAttribute(String name) {
144                return attributes.get(name);
145            }
146            
147            public void addAttribute(String name, String value) {
148                attributes.put(name, value);
149            }
150            
151            public Map<String, String> getDirectives() {
152                return directives;
153            }
154            
155            public String getDirective(String name) {
156                return directives.get(name);
157            }
158            
159            public void addDirective(String name, String value) {
160                directives.put(name, value);
161            }
162    
163            @Override
164            public String toString() {
165                return "HeaderElement [path=" + path + ", attributes=" + attributes + ", directives=" + directives + "]";
166            }
167    
168            @Override
169            public int hashCode() {
170                final int prime = 31;
171                int result = 1;
172                result = prime * result + ((attributes == null) ? 0 : attributes.hashCode());
173                result = prime * result + ((directives == null) ? 0 : directives.hashCode());
174                result = prime * result + ((path == null) ? 0 : path.hashCode());
175                return result;
176            }
177    
178            @Override
179            public boolean equals(Object obj) {
180                if (this == obj)
181                    return true;
182                if (obj == null)
183                    return false;
184                if (getClass() != obj.getClass())
185                    return false;
186                HeaderElement other = (HeaderElement) obj;
187                if (attributes == null) {
188                    if (other.attributes != null)
189                        return false;
190                } else if (!attributes.equals(other.attributes))
191                    return false;
192                if (directives == null) {
193                    if (other.directives != null)
194                        return false;
195                } else if (!directives.equals(other.directives))
196                    return false;
197                if (path == null) {
198                    if (other.path != null)
199                        return false;
200                } else if (!path.equals(other.path))
201                    return false;
202                return true;
203            }                
204        }
205    }