Richard S. Hall | 85bafab | 2009-07-13 13:25:46 +0000 | [diff] [blame^] | 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 | |
| 20 | package org.cauldron.bld.obr; |
| 21 | |
| 22 | import java.io.File; |
| 23 | import java.net.URI; |
| 24 | import java.net.URISyntaxException; |
| 25 | import java.net.URL; |
| 26 | import java.util.ArrayList; |
| 27 | import java.util.Arrays; |
| 28 | import java.util.HashSet; |
| 29 | import java.util.LinkedList; |
| 30 | import java.util.List; |
| 31 | |
| 32 | import org.cauldron.sigil.model.ModelElementFactory; |
| 33 | import org.cauldron.sigil.model.common.LDAPExpr; |
| 34 | import org.cauldron.sigil.model.common.LDAPParseException; |
| 35 | import org.cauldron.sigil.model.common.LDAPParser; |
| 36 | import org.cauldron.sigil.model.common.SimpleTerm; |
| 37 | import org.cauldron.sigil.model.common.VersionRange; |
| 38 | import org.cauldron.sigil.model.eclipse.ISigilBundle; |
| 39 | import org.cauldron.sigil.model.osgi.IBundleModelElement; |
| 40 | import org.cauldron.sigil.model.osgi.IPackageExport; |
| 41 | import org.cauldron.sigil.model.osgi.IPackageImport; |
| 42 | import org.cauldron.sigil.model.osgi.IRequiredBundle; |
| 43 | import org.eclipse.core.runtime.IPath; |
| 44 | import org.eclipse.core.runtime.Path; |
| 45 | import org.osgi.framework.Version; |
| 46 | import org.xml.sax.Attributes; |
| 47 | import org.xml.sax.Locator; |
| 48 | import org.xml.sax.SAXException; |
| 49 | import org.xml.sax.SAXParseException; |
| 50 | import org.xml.sax.helpers.DefaultHandler; |
| 51 | |
| 52 | final class OBRHandler extends DefaultHandler { |
| 53 | private static final String PACKAGE = "package"; |
| 54 | private static final String URI = "uri"; |
| 55 | private static final String PRESENTATION_NAME = "presentationname"; |
| 56 | private static final String VERSION = "version"; |
| 57 | private static final String SYMBOLIC_NAME = "symbolicname"; |
| 58 | private final File cacheDir; |
| 59 | private final URL obrURL; |
| 60 | private final OBRListener listener; |
| 61 | |
| 62 | private HashSet<String> sanity = new HashSet<String>(); |
| 63 | private Locator locator; |
| 64 | private ISigilBundle bundle; |
| 65 | private IPackageExport export; |
| 66 | |
| 67 | public OBRHandler(URL obrURL, File bundleCache, OBRListener listener) { |
| 68 | this.obrURL = obrURL; |
| 69 | this.cacheDir = bundleCache; |
| 70 | this.listener = listener; |
| 71 | } |
| 72 | |
| 73 | public void setDocumentLocator (Locator locator) { |
| 74 | this.locator = locator; |
| 75 | } |
| 76 | |
| 77 | public void startElement (String uri, String localName, |
| 78 | String qName, Attributes attributes) throws SAXException { |
| 79 | if ( "resource".equals( qName ) ) { |
| 80 | startResource(attributes); |
| 81 | } |
| 82 | else if ( "capability".equals( qName ) ) { |
| 83 | startCapability(attributes); |
| 84 | } |
| 85 | else if( "require".equals( qName ) ) { |
| 86 | startRequire(attributes); |
| 87 | } |
| 88 | else if ( "p".equals( qName ) ) { |
| 89 | startProperty(attributes); |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | public void endElement (String uri, String localName, String qName) |
| 94 | throws SAXException { |
| 95 | if ( "resource".equals( qName ) ) { |
| 96 | endResource(); |
| 97 | } |
| 98 | else if ( "capability".equals( qName ) ) { |
| 99 | endCapability(); |
| 100 | } |
| 101 | else if( "require".equals( qName ) ) { |
| 102 | endRequire(); |
| 103 | } |
| 104 | else if ( "p".equals( qName ) ) { |
| 105 | endProperty(); |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | private void startResource(Attributes attributes) throws SAXException { |
| 110 | try { |
| 111 | String uri = attributes.getValue("", URI); |
| 112 | if ( uri.endsWith( ".jar" ) ) { |
| 113 | if ( !sanity.add(uri) ) { |
| 114 | throw new RuntimeException(uri); |
| 115 | } |
| 116 | ISigilBundle b = ModelElementFactory.getInstance().newModelElement(ISigilBundle.class); |
| 117 | IBundleModelElement info = ModelElementFactory.getInstance().newModelElement(IBundleModelElement.class); |
| 118 | info.setSymbolicName(attributes.getValue("", SYMBOLIC_NAME)); |
| 119 | info.setVersion(new Version(attributes.getValue("", VERSION))); |
| 120 | info.setName(attributes.getValue("", PRESENTATION_NAME)); |
| 121 | URI l = makeAbsolute(uri); |
| 122 | info.setUpdateLocation(l); |
| 123 | b.setLocation(cachePath(info)); |
| 124 | b.setBundleInfo(info); |
| 125 | bundle = b; |
| 126 | } |
| 127 | } |
| 128 | catch (Exception e) { |
| 129 | throw new SAXParseException("Failed to build bundle info", locator, e); |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | private URI makeAbsolute(String uri) throws URISyntaxException { |
| 134 | URI l = new URI(uri); |
| 135 | if ( !l.isAbsolute() ) { |
| 136 | String base = obrURL.toExternalForm(); |
| 137 | int i = base.lastIndexOf("/"); |
| 138 | if ( i != -1 ) { |
| 139 | base = base.substring(0, i); |
| 140 | l = new URI( base + (uri.startsWith("/") ? "" : "/") + uri ); |
| 141 | } |
| 142 | } |
| 143 | return l; |
| 144 | } |
| 145 | |
| 146 | private IPath cachePath(IBundleModelElement info) { |
| 147 | return new Path(cacheDir.getAbsolutePath()).append( info.getSymbolicName() + "_" + info.getVersion() + ".jar" ); |
| 148 | } |
| 149 | |
| 150 | private void startCapability(Attributes attributes) { |
| 151 | if ( bundle != null ) { |
| 152 | if ( PACKAGE.equals( attributes.getValue("", "name") ) ) { |
| 153 | export = ModelElementFactory.getInstance().newModelElement(IPackageExport.class); |
| 154 | } |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | private void startRequire(Attributes attributes) throws SAXParseException { |
| 159 | if ( bundle != null ) { |
| 160 | String name = attributes.getValue("name"); |
| 161 | if ( PACKAGE.equals( name ) ) { |
| 162 | IPackageImport pi = ModelElementFactory.getInstance().newModelElement(IPackageImport.class); |
| 163 | try { |
| 164 | LDAPExpr expr = LDAPParser.parseExpression(attributes.getValue("filter") ); |
| 165 | pi.setPackageName(decodePackage(expr, locator)); |
| 166 | pi.setVersions(decodeVersions(expr, locator)); |
| 167 | pi.setOptional(Boolean.valueOf(attributes.getValue("optional"))); |
| 168 | bundle.getBundleInfo().addImport(pi); |
| 169 | } catch (LDAPParseException e) { |
| 170 | throw new SAXParseException("Failed to parse filter", locator, e); |
| 171 | } |
| 172 | } |
| 173 | else if ( "bundle".equals( name ) ) { |
| 174 | IRequiredBundle b = ModelElementFactory.getInstance().newModelElement(IRequiredBundle.class); |
| 175 | try { |
| 176 | LDAPExpr expr = LDAPParser.parseExpression(attributes.getValue("filter") ); |
| 177 | b.setSymbolicName(decodeSymbolicName(expr, locator)); |
| 178 | b.setVersions(decodeVersions(expr, locator)); |
| 179 | b.setOptional(Boolean.valueOf(attributes.getValue("optional"))); |
| 180 | bundle.getBundleInfo().addRequiredBundle(b); |
| 181 | } catch (Exception e) { |
| 182 | System.err.println( "Failed to parse filter in bundle " + bundle.getBundleInfo().getSymbolicName() ); |
| 183 | throw new SAXParseException("Failed to parse filter in bundle " + bundle.getBundleInfo().getSymbolicName(), locator, e); |
| 184 | } |
| 185 | } |
| 186 | //else if ( "ee".equals( name ) ) { |
| 187 | // TODO ignore for now... |
| 188 | //} |
| 189 | //else if ( "service".equals( name ) ) { |
| 190 | // TODO ignore for now... |
| 191 | //} |
| 192 | //else { |
| 193 | // for ( int i = 0; i < attributes.getLength(); i++ ) { |
| 194 | // System.out.println( "Found requirement " + attributes.getValue(i) ); |
| 195 | // } |
| 196 | //} |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | private static VersionRange decodeVersions(LDAPExpr expr, Locator locator) throws SAXParseException { |
| 201 | try { |
| 202 | return VersionRangeHelper.decodeVersions(expr); |
| 203 | } |
| 204 | catch (NumberFormatException e) { |
| 205 | throw new SAXParseException(e.getMessage(), locator); |
| 206 | } |
| 207 | } |
| 208 | |
| 209 | private void startProperty(Attributes attributes) { |
| 210 | if ( export != null ) { |
| 211 | String name = attributes.getValue("", "n"); |
| 212 | String value = attributes.getValue("", "v"); |
| 213 | if ( PACKAGE.equals( name ) ) { |
| 214 | export.setPackageName(value); |
| 215 | } |
| 216 | else if ( "uses".equals( name ) ) { |
| 217 | String[] split = value.split(","); |
| 218 | export.setUses( Arrays.asList(split) ); |
| 219 | } |
| 220 | else if ( "version".equals( name ) ) { |
| 221 | export.setVersion( new Version(value) ); |
| 222 | } |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | private void endResource() { |
| 227 | if ( bundle != null ) { |
| 228 | listener.handleBundle(bundle); |
| 229 | bundle = null; |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | private void endCapability() { |
| 234 | if ( bundle != null ) { |
| 235 | if ( export != null ) { |
| 236 | bundle.getBundleInfo().addExport(export); |
| 237 | export = null; |
| 238 | } |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | private void endRequire() { |
| 243 | // TODO Auto-generated method stub |
| 244 | |
| 245 | } |
| 246 | |
| 247 | private void endProperty() { |
| 248 | // TODO Auto-generated method stub |
| 249 | |
| 250 | } |
| 251 | |
| 252 | private static String decodePackage(LDAPExpr expr, Locator locator) throws SAXParseException { |
| 253 | ArrayList<SimpleTerm> terms = new ArrayList<SimpleTerm>(1); |
| 254 | |
| 255 | findTerms("package", expr, terms); |
| 256 | |
| 257 | if ( terms.isEmpty() ) { |
| 258 | throw new SAXParseException("Missing name filter in " + expr, locator); |
| 259 | } |
| 260 | |
| 261 | return terms.get(0).getRval(); |
| 262 | } |
| 263 | |
| 264 | private static String decodeSymbolicName(LDAPExpr expr, Locator locator) throws SAXParseException { |
| 265 | ArrayList<SimpleTerm> terms = new ArrayList<SimpleTerm>(1); |
| 266 | |
| 267 | findTerms("symbolicname", expr, terms); |
| 268 | |
| 269 | if ( terms.isEmpty() ) { |
| 270 | throw new SAXParseException("Missing name filter in " + expr, locator); |
| 271 | } |
| 272 | |
| 273 | return terms.get(0).getRval(); |
| 274 | } |
| 275 | |
| 276 | private static void findTerms(String string, LDAPExpr expr, List<SimpleTerm> terms) throws SAXParseException { |
| 277 | if ( expr instanceof SimpleTerm ) { |
| 278 | SimpleTerm term = (SimpleTerm) expr; |
| 279 | if ( term.getName().equals(string) ) { |
| 280 | terms.add(term); |
| 281 | } |
| 282 | } |
| 283 | else { |
| 284 | for ( LDAPExpr c : expr.getChildren() ) { |
| 285 | findTerms(string, c, terms); |
| 286 | } |
| 287 | } |
| 288 | } |
| 289 | } |