content: /*
   *  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
   *
   *      https://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.ivy.osgi.obr.xml;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.net.URI;
  import java.net.URISyntaxException;
  import java.text.ParseException;
  
  import javax.xml.parsers.ParserConfigurationException;
  
  import org.apache.ivy.osgi.core.BundleArtifact;
  import org.apache.ivy.osgi.core.BundleInfo;
  import org.apache.ivy.osgi.core.ExecutionEnvironmentProfileProvider;
  import org.apache.ivy.osgi.filter.OSGiFilter;
  import org.apache.ivy.osgi.filter.OSGiFilterParser;
  import org.apache.ivy.osgi.repo.BundleRepoDescriptor;
  import org.apache.ivy.osgi.util.DelegatingHandler;
  import org.apache.ivy.osgi.util.Version;
  import org.apache.ivy.util.Message;
  import org.apache.ivy.util.XMLHelper;
  import org.xml.sax.Attributes;
  import org.xml.sax.SAXException;
  import org.xml.sax.SAXParseException;
  
  public class OBRXMLParser {
  
      public static BundleRepoDescriptor parse(URI baseUri, InputStream in) throws IOException,
              SAXException {
          RepositoryHandler handler = new RepositoryHandler(baseUri);
          try {
              XMLHelper.parse(in, null, handler, null);
          } catch (ParserConfigurationException e) {
              throw new SAXException(e);
          }
          return handler.repo;
      }
  
      static class RepositoryHandler extends DelegatingHandler {
  
          static final String REPOSITORY = "repository";
  
          static final String LASTMODIFIED = "lastmodified";
  
          static final String NAME = "name";
  
          BundleRepoDescriptor repo;
  
          private final URI baseUri;
  
          public RepositoryHandler(URI baseUri) {
              super(REPOSITORY);
              this.baseUri = baseUri;
              addChild(new ResourceHandler(), new ChildElementHandler<ResourceHandler>() {
                  @Override
                  public void childHandled(ResourceHandler child) {
                      repo.addBundle(child.bundleInfo);
                  }
              });
          }
  
          @Override
          protected void handleAttributes(Attributes atts) {
              repo = new BundleRepoDescriptor(baseUri,
                      ExecutionEnvironmentProfileProvider.getInstance());
  
              repo.setName(atts.getValue(NAME));
  
              repo.setLastModified(atts.getValue(LASTMODIFIED));
          }
      }
  
      static class ResourceHandler extends DelegatingHandler {
  
          private static final String DEFAULT_VERSION = "1.0.0";
  
          static final String RESOURCE = "resource";
  
          static final String ID = "id";
  
          static final String PRESENTATION_NAME = "presentationname";
  
          static final String SYMBOLIC_NAME = "symbolicname";
  
          static final String URI = "uri";
  
          static final String VERSION = "version";
  
          BundleInfo bundleInfo;
  
          public ResourceHandler() {
              super(RESOURCE);
  
              setSkipOnError(true); // if anything bad happen in any children, just ignore the
                                    // resource
  
              addChild(new ResourceSourceHandler(), new ChildElementHandler<ResourceSourceHandler>() {
                  @Override
                  public void childHandled(ResourceSourceHandler child) {
                      String uri = child.getBufferedChars().trim();
                      if (!uri.endsWith(".jar")) {
                          // the maven plugin is putting some useless source url sometimes...
                          log(Message.MSG_WARN,
                              "A source uri is suspect, it is not ending with .jar, it is probably"
                                      + " a pointer to a download page. Ignoring it.");
                          return;
                      }
                      try {
                          bundleInfo.addArtifact(new BundleArtifact(true, new URI(uri), null));
                      } catch (URISyntaxException e) {
                          log(Message.MSG_WARN, "Incorrect uri " + uri + ". The source of "
                                  + bundleInfo.getSymbolicName() + " is then ignored.");
                          return;
                      }
                  }
              });
              addChild(new ResourceDescriptionHandler(),
                  new ChildElementHandler<ResourceDescriptionHandler>() {
                      @Override
                      public void childHandled(ResourceDescriptionHandler child) {
                          bundleInfo.setDescription(child.getBufferedChars().trim());
                      }
                  });
              addChild(new ResourceDocumentationHandler(),
                  new ChildElementHandler<ResourceDocumentationHandler>() {
                      @Override
                      public void childHandled(ResourceDocumentationHandler child) {
                          bundleInfo.setDocumentation(child.getBufferedChars().trim());
                      }
                  });
              addChild(new ResourceLicenseHandler(),
                  new ChildElementHandler<ResourceLicenseHandler>() {
                      @Override
                      public void childHandled(ResourceLicenseHandler child) {
                          bundleInfo.setLicense(child.getBufferedChars().trim());
                      }
                  });
              addChild(new ResourceSizeHandler(), new ChildElementHandler<ResourceSizeHandler>() {
                  @Override
                  public void childHandled(ResourceSizeHandler child) {
                      String size = child.getBufferedChars().trim();
                      try {
                          bundleInfo.setSize(Integer.valueOf(size));
                      } catch (NumberFormatException e) {
                          log(Message.MSG_WARN,
                              "Invalid size for the bundle " + bundleInfo.getSymbolicName() + ": "
                                      + size + ". This size is then ignored.");
                      }
                  }
              });
              addChild(new CapabilityHandler(), new ChildElementHandler<CapabilityHandler>() {
                  @Override
                  public void childHandled(CapabilityHandler child) throws SAXParseException {
  
                      try {
                          CapabilityAdapter.adapt(bundleInfo, child.capability);
                      } catch (ParseException e) {
                          throw new SAXParseException("Invalid capability: " + e.getMessage(), child
                                  .getLocator());
                      }
                  }
              });
              addChild(new RequireHandler(), new ChildElementHandler<RequireHandler>() {
                  @Override
                  public void childHandled(RequireHandler child) throws SAXParseException {
                      try {
                          RequirementAdapter.adapt(bundleInfo, child.requirement);
                      } catch (UnsupportedFilterException e) {
                          throw new SAXParseException("Unsupported requirement filter: "
                                  + child.filter + " (" + e.getMessage() + ")", getLocator());
                      } catch (ParseException e) {
                          throw new SAXParseException(
                                  "Error in the requirement filter on the bundle: " + e.getMessage(),
                                  getLocator());
                      }
                  }
              });
              addChild(new ExtendHandler(), new ChildElementHandler<ExtendHandler>() {
                  @Override
                  public void childHandled(ExtendHandler child) throws SAXParseException {
                      // TODO handle fragment host
                  }
              });
          }
  
          @Override
          protected void handleAttributes(Attributes atts) throws SAXException {
              String symbolicname = atts.getValue(SYMBOLIC_NAME);
              if (symbolicname == null) {
                  log(Message.MSG_ERR, "Resource with no symbolic name, skipping it.");
                  skip();
                  return;
              }
  
              String v = getOptionalAttribute(atts, VERSION, DEFAULT_VERSION);
              Version version = new Version(v);
              bundleInfo = new BundleInfo(symbolicname, version);
              bundleInfo.setPresentationName(atts.getValue(PRESENTATION_NAME));
              String uri = atts.getValue(URI);
              if (uri != null) {
                  try {
                      bundleInfo.addArtifact(new BundleArtifact(false, new URI(uri), null));
                  } catch (URISyntaxException e) {
                      log(Message.MSG_ERR, "Incorrect uri " + uri + ". The resource " + symbolicname
                              + " is then ignored.");
                      skip();
                      return;
                  }
              }
              bundleInfo.setId(atts.getValue(ID));
          }
  
          @Override
          protected String getCurrentElementIdentifier() {
              return bundleInfo.getSymbolicName() + "/" + bundleInfo.getVersion();
          }
  
      }
  
      static class ResourceSourceHandler extends DelegatingHandler {
  
          static final String SOURCE = "source";
  
          public ResourceSourceHandler() {
              super(SOURCE);
              setBufferingChar(true);
          }
  
      }
  
      static class ResourceDescriptionHandler extends DelegatingHandler {
  
          static final String DESCRIPTION = "description";
  
          public ResourceDescriptionHandler() {
              super(DESCRIPTION);
              setBufferingChar(true);
          }
  
      }
  
      static class ResourceDocumentationHandler extends DelegatingHandler {
  
          static final String DOCUMENTATION = "documentation";
  
          public ResourceDocumentationHandler() {
              super(DOCUMENTATION);
              setBufferingChar(true);
          }
      }
  
      static class ResourceLicenseHandler extends DelegatingHandler {
  
          static final String LICENSE = "license";
  
          public ResourceLicenseHandler() {
              super(LICENSE);
              setBufferingChar(true);
          }
  
      }
  
      static class ResourceSizeHandler extends DelegatingHandler {
  
          static final String SIZE = "size";
  
          public ResourceSizeHandler() {
              super(SIZE);
              setBufferingChar(true);
          }
      }
  
      static class CapabilityHandler extends DelegatingHandler {
  
          static final String CAPABILITY = "capability";
  
          static final String NAME = "name";
  
          Capability capability;
  
          public CapabilityHandler() {
              super(CAPABILITY);
              addChild(new CapabilityPropertyHandler(),
                  new ChildElementHandler<CapabilityPropertyHandler>() {
                      @Override
                      public void childHandled(CapabilityPropertyHandler child) {
                          String name = child.name;
                          String value = child.value;
                          String type = child.type;
  
                          capability.addProperty(name, value, type);
                      }
                  });
          }
  
          @Override
          protected void handleAttributes(Attributes atts) throws SAXException {
              String name = getRequiredAttribute(atts, NAME);
              capability = new Capability(name);
          }
  
      }
  
      static class CapabilityPropertyHandler extends DelegatingHandler {
  
          static final String CAPABILITY_PROPERTY = "p";
  
          static final String NAME = "n";
  
          static final String VALUE = "v";
  
          static final String TYPE = "t";
  
          String name;
  
          String value;
  
          String type;
  
          public CapabilityPropertyHandler() {
              super(CAPABILITY_PROPERTY);
          }
  
          @Override
          protected void handleAttributes(Attributes atts) throws SAXException {
              name = getRequiredAttribute(atts, NAME);
              value = getRequiredAttribute(atts, VALUE);
              type = atts.getValue(TYPE);
          }
      }
  
      static class AbstractRequirementHandler extends DelegatingHandler {
  
          static final String NAME = "name";
  
          static final String OPTIONAL = "optional";
  
          static final String MULTIPLE = "multiple";
  
          static final String FILTER = "filter";
  
          Requirement requirement;
  
          OSGiFilter filter;
  
          public AbstractRequirementHandler(String name) {
              super(name);
          }
  
          @Override
          protected void handleAttributes(Attributes atts) throws SAXException {
              String name = getRequiredAttribute(atts, NAME);
  
              String filterText = atts.getValue(FILTER);
              filter = null;
              if (filterText != null) {
                  try {
                      filter = OSGiFilterParser.parse(filterText);
                  } catch (ParseException e) {
                      throw new SAXParseException("Requirement with ill-formed filter: " + filterText,
                              getLocator());
                  }
              }
  
              Boolean optional = getOptionalBooleanAttribute(atts, OPTIONAL, null);
              Boolean multiple = getOptionalBooleanAttribute(atts, MULTIPLE, null);
  
              requirement = new Requirement(name, filter);
              if (optional != null) {
                  requirement.setOptional(optional);
              }
              if (multiple != null) {
                  requirement.setMultiple(multiple);
              }
          }
  
      }
  
      static class RequireHandler extends AbstractRequirementHandler {
  
          static final String REQUIRE = "require";
  
          public RequireHandler() {
              super(REQUIRE);
          }
  
      }
  
      static class ExtendHandler extends AbstractRequirementHandler {
  
          static final String EXTEND = "extend";
  
          public ExtendHandler() {
              super(EXTEND);
          }
  
      }
  }
  