blob: 78213efb537c1ae44045d484fa24edec62ccc705 [file] [log] [blame]
/*
* 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
*
* http://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.felix.scr.impl;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
import org.apache.felix.scr.impl.parser.KXml2SAXHandler;
import org.apache.felix.scr.impl.parser.ParseException;
import org.osgi.framework.Bundle;
/**
*
*
*/
public class XmlHandler implements KXml2SAXHandler
{
public static final String NAMESPACE_URI = "http://www.osgi.org/xmlns/scr/v1.0.0";
// the bundle containing the XML resource being parsed
private Bundle m_bundle;
// A reference to the current component
private ComponentMetadata m_currentComponent;
// The current service
private ServiceMetadata m_currentService;
// A list of component descriptors contained in the file
private List m_components = new ArrayList();
// PropertyMetaData whose value attribute is missing, hence has element data
private PropertyMetadata m_pendingProperty;
/** Flag for detecting the first element. */
protected boolean firstElement = true;
/** Override namespace. */
protected String overrideNamespace;
/** Flag for elements inside a component element */
protected boolean isComponent = false;
// creates an instance with the bundle owning the component descriptor
// file parsed by this instance
XmlHandler( Bundle bundle )
{
m_bundle = bundle;
}
/**
* Method called when a tag opens
*
* @param uri
* @param localName
* @param attrib
* @exception ParseException
**/
public void startElement( String uri, String localName, Properties attrib ) throws ParseException
{
// according to the spec, the elements should have the namespace,
// except when the root element is the "component" element
// So we check this for the first element, we receive.
if ( firstElement )
{
firstElement = false;
if ( localName.equals( "component" ) && "".equals( uri ) )
{
overrideNamespace = NAMESPACE_URI;
}
}
if ( overrideNamespace != null && "".equals( uri ) )
{
uri = overrideNamespace;
}
// FELIX-695: however the spec also states that the inner elements
// of a component are unqualified, so they don't have
// the namespace - we allow both: with or without namespace!
if ( this.isComponent && "".equals(uri) )
{
uri = NAMESPACE_URI;
}
// from now on uri points to the namespace
if ( NAMESPACE_URI.equals( uri ) )
{
try
{
// 112.4.3 Component Element
if ( localName.equals( "component" ) )
{
this.isComponent = true;
// Create a new ComponentMetadata
m_currentComponent = new ComponentMetadata();
// name attribute is mandatory
m_currentComponent.setName( attrib.getProperty( "name" ) );
// enabled attribute is optional
if ( attrib.getProperty( "enabled" ) != null )
{
m_currentComponent.setEnabled( attrib.getProperty( "enabled" ).equals( "true" ) );
}
// immediate attribute is optional
if ( attrib.getProperty( "immediate" ) != null )
{
m_currentComponent.setImmediate( attrib.getProperty( "immediate" ).equals( "true" ) );
}
// factory attribute is optional
if ( attrib.getProperty( "factory" ) != null )
{
m_currentComponent.setFactoryIdentifier( attrib.getProperty( "factory" ) );
}
// Add this component to the list
m_components.add( m_currentComponent );
}
// 112.4.4 Implementation
else if ( localName.equals( "implementation" ) )
{
// Set the implementation class name (mandatory)
m_currentComponent.setImplementationClassName( attrib.getProperty( "class" ) );
}
// 112.4.5 [...] Property Elements
else if ( localName.equals( "property" ) )
{
PropertyMetadata prop = new PropertyMetadata();
// name attribute is mandatory
prop.setName( attrib.getProperty( "name" ) );
// type attribute is optional
if ( attrib.getProperty( "type" ) != null )
{
prop.setType( attrib.getProperty( "type" ) );
}
// 112.4.5: If the value attribute is specified, the body of the element is ignored.
if ( attrib.getProperty( "value" ) != null )
{
prop.setValue( attrib.getProperty( "value" ) );
m_currentComponent.addProperty( prop );
}
else
{
// hold the metadata pending
m_pendingProperty = prop;
}
}
// 112.4.5 Properties [...] Elements
else if ( localName.equals( "properties" ) )
{
readPropertiesEntry( attrib.getProperty( "entry" ) );
}
// 112.4.6 Service Element
else if ( localName.equals( "service" ) )
{
m_currentService = new ServiceMetadata();
// servicefactory attribute is optional
if ( attrib.getProperty( "servicefactory" ) != null )
{
m_currentService.setServiceFactory( attrib.getProperty( "servicefactory" ).equals( "true" ) );
}
m_currentComponent.setService( m_currentService );
}
else if ( localName.equals( "provide" ) )
{
m_currentService.addProvide( attrib.getProperty( "interface" ) );
}
// 112.4.7 Reference element
else if ( localName.equals( "reference" ) )
{
ReferenceMetadata ref = new ReferenceMetadata();
ref.setName( attrib.getProperty( "name" ) );
ref.setInterface( attrib.getProperty( "interface" ) );
// Cardinality
if ( attrib.getProperty( "cardinality" ) != null )
{
ref.setCardinality( attrib.getProperty( "cardinality" ) );
}
if ( attrib.getProperty( "policy" ) != null )
{
ref.setPolicy( attrib.getProperty( "policy" ) );
}
//if
ref.setTarget( attrib.getProperty( "target" ) );
ref.setBind( attrib.getProperty( "bind" ) );
ref.setUnbind( attrib.getProperty( "unbind" ) );
m_currentComponent.addDependency( ref );
}
}
catch ( Exception ex )
{
ex.printStackTrace();
throw new ParseException( "Exception during parsing", ex );
}
}
}
/**
* Method called when a tag closes
*
* @param uri
* @param localName
*/
public void endElement( String uri, String localName )
{
if ( overrideNamespace != null && "".equals( uri ) )
{
uri = overrideNamespace;
}
if ( this.isComponent && "".equals(uri) )
{
uri = NAMESPACE_URI;
}
if ( NAMESPACE_URI.equals( uri ) )
{
if ( localName.equals( "component" ) )
{
this.isComponent = false;
// When the closing tag for a component is found, the component is validated to check if
// the implementation class has been set
m_currentComponent.validate();
}
else if ( localName.equals( "property" ) && m_pendingProperty != null )
{
// 112.4.5 body expected to contain property value
// if so, the m_pendingProperty field would be null
// currently, we just ignore this situation
m_pendingProperty = null;
}
}
}
/**
* Called to retrieve the service descriptors
*
* @return A list of service descriptors
*/
List getComponentMetadataList()
{
return m_components;
}
/**
* @see org.apache.felix.scr.impl.parser.KXml2SAXHandler#characters(java.lang.String)
*/
public void characters( String text )
{
// 112.4.5 If the value attribute is not specified, the body must contain one or more values
if ( m_pendingProperty != null )
{
m_pendingProperty.setValues( text );
m_currentComponent.addProperty( m_pendingProperty );
m_pendingProperty = null;
}
}
/**
* @see org.apache.felix.scr.impl.parser.KXml2SAXHandler#processingInstruction(java.lang.String, java.lang.String)
*/
public void processingInstruction( String target, String data )
{
// Not used
}
/**
* @see org.apache.felix.scr.impl.parser.KXml2SAXHandler#setLineNumber(int)
*/
public void setLineNumber( int lineNumber )
{
// Not used
}
/**
* @see org.apache.felix.scr.impl.parser.KXml2SAXHandler#setColumnNumber(int)
*/
public void setColumnNumber( int columnNumber )
{
// Not used
}
/**
* Reads the name property file from the bundle owning this descriptor. All
* properties read from the properties file are added to the current
* component's property meta data list.
*
* @param entryName The name of the bundle entry containing the propertes
* to be added. This must not be <code>null</code>.
*
* @throws ParseException If the entry name is <code>null</code> or no
* entry with the given name exists in the bundle or an error occurrs
* reading the properties file.
*/
private void readPropertiesEntry( String entryName ) throws ParseException
{
if ( entryName == null )
{
throw new ParseException( "Missing entry attribute of properties element", null );
}
URL entryURL = m_bundle.getEntry( entryName );
if ( entryURL == null )
{
throw new ParseException( "Missing bundle entry " + entryName, null );
}
Properties props = new Properties();
InputStream entryStream = null;
try
{
entryStream = entryURL.openStream();
props.load( entryStream );
}
catch ( IOException ioe )
{
throw new ParseException( "Failed to read properties entry " + entryName, ioe );
}
finally
{
if ( entryStream != null )
{
try
{
entryStream.close();
}
catch ( IOException ignore )
{
// don't care
}
}
}
// create PropertyMetadata for the properties from the file
for ( Iterator pi = props.entrySet().iterator(); pi.hasNext(); )
{
Map.Entry pEntry = ( Map.Entry ) pi.next();
PropertyMetadata prop = new PropertyMetadata();
prop.setName( String.valueOf( pEntry.getKey() ) );
prop.setValue( String.valueOf( pEntry.getValue() ) );
m_currentComponent.addProperty( prop );
}
}
}