blob: f769b9ec4e4f71e1688d0a7d6ddaa43d6a1b2598 [file] [log] [blame]
/*
* Copyright (c) 2020 Kentyou.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Kentyou - initial API and implementation
*/
package org.eclipse.sensinact.gateway.generic.parser;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.eclipse.sensinact.gateway.common.bundle.Mediator;
import org.eclipse.sensinact.gateway.core.ActionResource;
import org.eclipse.sensinact.gateway.core.PropertyResource;
import org.eclipse.sensinact.gateway.core.Resource;
import org.eclipse.sensinact.gateway.core.ResourceConfig;
import org.eclipse.sensinact.gateway.core.SensorDataResource;
import org.eclipse.sensinact.gateway.core.ServiceProvider;
import org.eclipse.sensinact.gateway.core.StateVariableResource;
import org.eclipse.sensinact.gateway.core.TypeConfig;
import org.eclipse.sensinact.gateway.generic.ExtResourceConfig;
import org.eclipse.sensinact.gateway.generic.ExtResourceImpl;
import org.eclipse.sensinact.gateway.util.xml.AbstractContentHandler;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
/**
* Extended {@link AbstractContentHandler} implementation for xml file
* describing a collection of {@link ResourceInfoDefinition}s
*
* @author <a href="mailto:christophe.munilla@cea.fr">Christophe Munilla</a>
*/
public class XmlResourceConfigHandler extends AbstractContentHandler<ResourceInfoDefinition> implements
RootXmlParsingContext{
static final String LOCATION_KEY_WORD = "LOCATION";
static final String BINARY_COMMAND_TYPE = "binaryHexContent";
static final String STRING_COMMAND_TYPE = "stringContent";
//private static final String UPDATE_ATTRIBUTE = "update";
private static final String DEFAULT_POLICY_IMPLEMENTATION = ExtResourceImpl.class.getCanonicalName();
public static final Class<StateVariableResource> DEFAULT_VARIABLE_INTERFACE = StateVariableResource.class;
public static final Class<PropertyResource> DEFAULT_PROPERTY_INTERFACE = PropertyResource.class;
public static final Class<SensorDataResource> DEFAULT_SENSOR_INTERFACE = SensorDataResource.class;
public static final Class<ActionResource> DEFAULT_ACTION_INTERFACE = ActionResource.class;
public static final String DEFAULT_VARIABLE_POLICY_INTERFACE = DEFAULT_VARIABLE_INTERFACE.getCanonicalName().intern();
public static final String DEFAULT_PROPERTY_POLICY_INTERFACE = DEFAULT_PROPERTY_INTERFACE.getCanonicalName().intern();
public static final String DEFAULT_SENSOR_POLICY_INTERFACE = DEFAULT_SENSOR_INTERFACE.getCanonicalName().intern();
public static final String DEFAULT_ACTION_POLICY_INTERFACE = DEFAULT_ACTION_INTERFACE.getCanonicalName().intern();
private Map<String, List<String>> profiles;
private LinkedList<PolicyDefinition> policies;
private Stack<CommandDefinition> commands;
private Stack<DeviceDefinition> devices;
private List<ExtResourceConfig> configs;
private Mediator mediator;
private boolean handleNoProfile;
XmlModelParsingContext parsingContext;
/**
* Constructor
*
* @param mediator the {@link Mediator} allowing the XmlResourceConfigHandler
* to be instantiated to interact with the OSGi host environment
*/
public XmlResourceConfigHandler(Mediator mediator) {
super();
this.mediator = mediator;
//this.mediator.setProperty("offset",new Integer(0));
this.commands = new Stack<CommandDefinition>();
this.devices = new Stack<DeviceDefinition>();
this.policies = new LinkedList<PolicyDefinition>();
this.profiles = new HashMap<String, List<String>>();
ResourceClassDefinition resourceClassDefinition = new ResourceClassDefinition(mediator, null);
resourceClassDefinition.end();
resourceClassDefinition.setResourceClassType(DEFAULT_POLICY_IMPLEMENTATION);
ResourceInterfaceDefinition resourceInterfaceDefinition = new ResourceInterfaceDefinition(mediator, null);
resourceInterfaceDefinition.setResourceInterfaceType(DEFAULT_VARIABLE_POLICY_INTERFACE);
PolicyDefinition policyDefinition = new PolicyDefinition(this.mediator, null);
policyDefinition.setPolicy(TypeConfig.Type.STATE_VARIABLE.name());
policyDefinition.setResourceClassDefinition(resourceClassDefinition);
policyDefinition.setResourceInterfaceDefinition(resourceInterfaceDefinition);
policyDefinition.setUpdatePolicy(Resource.UpdatePolicy.NONE.name());
policyDefinition.end();
this.policies.add(policyDefinition);
resourceInterfaceDefinition = new ResourceInterfaceDefinition(mediator, null);
resourceInterfaceDefinition.setResourceInterfaceType(DEFAULT_PROPERTY_POLICY_INTERFACE);
resourceInterfaceDefinition.end();
policyDefinition = new PolicyDefinition(this.mediator, null);
policyDefinition.setPolicy(TypeConfig.Type.PROPERTY.name());
policyDefinition.setResourceClassDefinition(resourceClassDefinition);
policyDefinition.setResourceInterfaceDefinition(resourceInterfaceDefinition);
policyDefinition.setUpdatePolicy(Resource.UpdatePolicy.NONE.name());
policyDefinition.end();
this.policies.add(policyDefinition);
resourceInterfaceDefinition = new ResourceInterfaceDefinition(mediator, null);
resourceInterfaceDefinition.setResourceInterfaceType(DEFAULT_SENSOR_POLICY_INTERFACE);
policyDefinition = new PolicyDefinition(this.mediator, null);
policyDefinition.setPolicy(TypeConfig.Type.SENSOR.name());
policyDefinition.setResourceClassDefinition(resourceClassDefinition);
policyDefinition.setResourceInterfaceDefinition(resourceInterfaceDefinition);
policyDefinition.setUpdatePolicy(Resource.UpdatePolicy.NONE.name());
policyDefinition.end();
this.policies.add(policyDefinition);
resourceInterfaceDefinition = new ResourceInterfaceDefinition(mediator, null);
resourceInterfaceDefinition.setResourceInterfaceType(DEFAULT_ACTION_POLICY_INTERFACE);
policyDefinition = new PolicyDefinition(this.mediator, null);
policyDefinition.setPolicy(TypeConfig.Type.ACTION.name());
policyDefinition.setResourceClassDefinition(resourceClassDefinition);
policyDefinition.setResourceInterfaceDefinition(resourceInterfaceDefinition);
policyDefinition.setUpdatePolicy(Resource.UpdatePolicy.NONE.name());
policyDefinition.end();
this.policies.add(policyDefinition);
}
/**
* Start of a "policy" XML node parsing
*
* @param atts the {@link Attributes} of the parsed XML node */
public void policyStart(Attributes atts) {
this.parsingContext = new PolicyDefinition(mediator,atts);
}
/**
* End of a "policy" XML node parsing
*/
public void policyEnd() {
PolicyDefinition policyDefinition = (PolicyDefinition)this.parsingContext;
this.parsingContext = null;
policyDefinition.mapTag(super.textContent.toString());
PolicyDefinition registered = this.removePolicy(policyDefinition.getPolicy().name());
Class<? extends ExtResourceImpl> implementationClass = policyDefinition.getPolicyImplementationClass();
if (implementationClass == null) {
policyDefinition.setPolicyImplementationClass(registered.getPolicyImplementationClass().getCanonicalName());
}
Class<? extends Resource> implementationInterface = policyDefinition.getPolicyImplementationInterface();
if (implementationInterface == null) {
policyDefinition.setPolicyImplementationInterface(registered.getPolicyImplementationInterface().getCanonicalName());
}
this.policies.add(policyDefinition);
}
/**
* Start of a "command" XML node parsing
*
* @param atts the {@link Attributes} of the parsed XML node */
public void commandStart(Attributes atts) {
CommandDefinition commandDefinition = new CommandDefinition(this.mediator, atts);
this.commands.push(commandDefinition);
this.parsingContext = commandDefinition;
}
/**
* Start of a "resourceInfos" XML node parsing
*
* @param atts the {@link Attributes} of the parsed XML node */
public void resourceInfosStart(Attributes atts) throws SAXException {
this.handleNoProfile = Boolean.parseBoolean(atts.getValue("empty_profile"));
}
/**
* End of a "resourceInfos" XML node parsing
*/
public void resourceInfosEnd() {
this.configs = new ArrayList<ExtResourceConfig>();
Iterator<ResourceInfoDefinition> iterator = super.stack.iterator();
while(iterator.hasNext()) {
ResourceInfoDefinition definition = iterator.next();
configs.add((ExtResourceConfig) definition.asResourceConfig());
}
}
/**
* Start of a "resourceInfo" XML node parsing
*
* @param atts the {@link Attributes} of the parsed XML node */
public ResourceInfoDefinition resourceInfoStart(Attributes atts) throws SAXException {
ResourceInfoDefinition definition = new ResourceInfoDefinition(mediator,this,atts);
this.parsingContext = definition;
return definition;
}
/**
* Start of a "service" XML node parsing
*
* @param atts the {@link Attributes} of the parsed XML node */
public void serviceStart(Attributes atts) {
String serviceName = atts.getValue("name");
if (serviceName != null) {
devices.peek().addService(serviceName);
}
}
/**
* Start of a "device" XML node parsing
*
* @param atts the {@link Attributes} of the parsed XML node */
public void deviceStart(Attributes atts) {
DeviceDefinition deviceDefinition = new DeviceDefinition(this.mediator, atts);
devices.push(deviceDefinition);
}
/**
* @inheritDoc
* @see org.eclipse.sensinact.gateway.util.frame.AbstractContentHandler#
* end(java.lang.String)
*/
@Override
public ResourceInfoDefinition end(String tag, String qname) {
ResourceInfoDefinition def = null;
Method endMethod = null;
Class<?> clazz = null;
Object subject = null;
XmlModelParsingContext parent = null;
XmlModelParsingContext context = getContext();
if(context != null) {
for(String escape :context.escaped()) {
if(tag.equals(escape)) {
return def;
}
}
context.end();
parent = getParentContext();
if(parent!= null) {
clazz = parent.getClass();
subject = parent;
}
}
if(subject==null) {
clazz = getClass();
subject = this;
}
try {
endMethod = clazz.getDeclaredMethod(qname.concat("End"));
def = (ResourceInfoDefinition) endMethod.invoke(subject);
} catch(Exception e) {
this.mediator.debug(e.getMessage());
}
if(parent!= null) {
parent.setNext(null);
} else {
this.parsingContext = null;
}
return def;
}
/**
* @inheritDoc
* @see org.eclipse.sensinact.gateway.util.frame.AbstractContentHandler#
* start(java.lang.String, org.xml.sax.Attributes)
*/
@Override
public ResourceInfoDefinition start(String tag, String qname, Attributes atts) throws SAXException {
ResourceInfoDefinition def = null;
Method startMethod = null;
Class<?> clazz = null;
Object subject = null;
XmlModelParsingContext context = getContext();
if(context != null) {
for(String escape :context.escaped()) {
if(tag.equals(escape)) {
return def;
}
}
clazz = context.getClass();
subject = context;
} else {
clazz = getClass();
subject = this;
}
try {
startMethod = clazz.getMethod(qname.concat("Start"), Attributes.class);
def = (ResourceInfoDefinition) startMethod.invoke(subject, atts);
} catch(Exception e) {
this.mediator.debug(e.getMessage());
}
return def;
}
/**
* @inheritDoc
* @see org.xml.sax.ContentHandler#characters(char[], int, int)
*/
public void characters(char[] chrs, int start, int length) throws SAXException {
XmlModelParsingContext context = getContext();
if(context != null) {
context.characters(chrs, start, length);
}
}
/**
* @inheritDoc
* @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
*/
public void ignorableWhitespace(char[] chrs, int start, int length) throws SAXException {
XmlModelParsingContext context = getContext();
if(context != null) {
context.ignorableWhitespace(chrs, start, length);
}
}
/**
* @inheritDoc
* @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, java.lang.String)
*/
public void processingInstruction(String target, String data) throws SAXException {
XmlModelParsingContext context = getContext();
if(context != null) {
context.processingInstruction(target, data);
}
}
/**
* @inheritDoc
* @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String)
*/
public void skippedEntity(String name) throws SAXException {
XmlModelParsingContext context = getContext();
if(context != null) {
context.skippedEntity(name);
}
}
/**
* Returns the {@link PolicyDefinition} instance wrapping the
* {@link ResourceTypeConf} whose name is passed as parameter
*
* @param policyName the name of the searched {@link ResourceTypeConf}
* @return the {@link PolicyDefinition} instance wrapping the
* {@link ResourceTypeConf} whose name is passed as
* parameter
*/
public PolicyDefinition getPolicy(String policyName) {
if (policyName != null) {
for (PolicyDefinition policyDefinition : this.policies) {
if (policyName.equals(policyDefinition.getPolicy().name())) {
return policyDefinition;
}
}
}
return null;
}
/**
* Returns the {@link PolicyDefinition} instance wrapping the
* {@link ResourceTypeConf} whose name is passed as parameter
*
* @param policyName the name of the searched {@link ResourceTypeConf}
* @return the {@link PolicyDefinition} instance wrapping the
* {@link ResourceTypeConf} whose name is passed as
* parameter
*/
private PolicyDefinition removePolicy(String policyName) {
if (policyName != null) {
int index = 0;
for(;index < policies.size();index++){
PolicyDefinition policyDefinition = this.policies.get(index);
if (policyName.equals(policyDefinition.getPolicy().name())) {
return this.policies.remove(index);
}
}
}
return null;
}
XmlModelParsingContext getContext() {
if(this.parsingContext == null) {
return null;
}
XmlModelParsingContext context = this.parsingContext;
while(context.next()!=null){
context = context.next();
}
return context;
}
XmlModelParsingContext getParentContext() {
if(this.parsingContext == null || this.parsingContext.next()== null) {
return null;
}
XmlModelParsingContext parent = null;
XmlModelParsingContext context = this.parsingContext;
while(context.next()!=null){
parent = context;
context = context.next();
}
return parent;
}
public void registerProfile(String[] profiles, String[] targets) {
if (profiles == null || profiles.length==0) {
return;
}
for(String profile :profiles) {
if("ANY_PROFILE".equals(profile)||(ResourceConfig.ALL_PROFILES.equals(profile) && !this.handleNoProfile)){
return;
}
}
for(String profile :profiles) {
List<String> services = this.profiles.get(profile);
if (services == null) {
services = new ArrayList<String>();
this.profiles.put(profile, services);
}
int targetIndex = 0;
int targetLength = targets == null ? 0 : targets.length;
for (; targetIndex < targetLength; targetIndex++) {
if (ServiceProvider.ADMINISTRATION_SERVICE_NAME.equals(targets[targetIndex])
|| services.contains(targets[targetIndex])) {
continue;
}
services.add(targets[targetIndex]);
}
}
}
/**
* Returns the Map where the key is a profile string name
* and the value is the list of service names associated
* to the specified profile
*
* @return the Map of profiles and their associated
* service names
*/
public Map<String, List<String>> getProfiles() {
return Collections.unmodifiableMap(this.profiles);
}
/**
* Returns an {@link Iterator} over {@link CommandDefinition}s
* referenced in the parsed xml file
*
* @return an {@link Iterator} over {@link CommandDefinition}s
* referenced in the parsed xml file
*/
public Commands getCommandDefinitions() {
return new Commands(this.commands);
}
/**
* Returns an {@link Iterator} over {@link DeviceDefinition}s
* referenced in the parsed xml file
*
* @return an {@link Iterator} over {@link DeviceDefinition}s
* referenced in the parsed xml file
*/
public FixedProviders getDeviceDefinitions() {
return new FixedProviders(this.devices);
}
/**
* Returns an {@link Iterator} over {@link ExtResourceConfig}s
* described in the parsed xml file
*
* @return an {@link Iterator} over {@link ExtResourceConfig}s
* described in the parsed xml file
*/
public Iterator<ExtResourceConfig> getXmlResourceConfigs() {
return this.configs.iterator();
}
}