/** * Copyright (c) 2014-2017 by the respective copyright holders. * 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 */ package org.eclipse.smarthome.core.thing.xml.internal; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.smarthome.config.core.ConfigDescription; import org.eclipse.smarthome.config.core.ConfigDescriptionProvider; import org.eclipse.smarthome.config.xml.AbstractXmlConfigDescriptionProvider; import org.eclipse.smarthome.config.xml.osgi.XmlDocumentBundleTracker; import org.eclipse.smarthome.config.xml.osgi.XmlDocumentProvider; import org.eclipse.smarthome.core.thing.binding.ThingTypeProvider; import org.eclipse.smarthome.core.thing.type.ChannelGroupType; import org.eclipse.smarthome.core.thing.type.ChannelType; import org.eclipse.smarthome.core.thing.type.ThingType; import org.osgi.framework.Bundle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.thoughtworks.xstream.converters.ConversionException; /** * The {@link ThingTypeXmlProvider} is responsible managing any created {@link ThingType} objects by a * {@link ThingDescriptionReader} for a certain * bundle. * <p> * This implementation registers each {@link ThingType} object at the {@link ThingTypeProvider} which is itself * registered as service at the <i>OSGi</i> service registry. If a configuration section is found, a * {@link ConfigDescription} object is registered at the {@link ConfigDescriptionProvider} which is itself registered as * service at the <i>OSGi</i> service registry. * <p> * The {@link ThingTypeXmlProvider} uses an internal cache consisting of {@link #thingTypeRefs}, * {@link #channelGroupTypeRefs}, {@link #channelGroupTypes} and {@link #channelTypes}. This cache is used to merge * first the {@link ChannelType} definitions with the {@link ChannelGroupTypeXmlResult} objects to create valid * {@link ChannelGroupType} objects. After that the {@link ChannelType} and the {@link ChannelGroupType} definitions are * used to merge with the {@link ThingTypeXmlResult} objects to create valid {@link ThingType} objects. After the merge * process has been finished, the cache is cleared again. The merge process is started when {@link #addingFinished()} is * invoked from the according {@link XmlDocumentBundleTracker}. * * @author Michael Grammling - Initial Contribution * @author Ivan Iliev - Added support for system wide channel types * * @see ThingTypeXmlProviderFactory */ public class ThingTypeXmlProvider implements XmlDocumentProvider<List<?>> { private Logger logger = LoggerFactory.getLogger(ThingTypeXmlProvider.class); private Bundle bundle; private AbstractXmlConfigDescriptionProvider configDescriptionProvider; private XmlThingTypeProvider thingTypeProvider; // temporary cache private List<ThingTypeXmlResult> thingTypeRefs; private List<ChannelGroupTypeXmlResult> channelGroupTypeRefs; private List<ChannelTypeXmlResult> channelTypeRefs; private XmlChannelTypeProvider channelTypeProvider; public ThingTypeXmlProvider(Bundle bundle, AbstractXmlConfigDescriptionProvider configDescriptionProvider, XmlThingTypeProvider thingTypeProvider, XmlChannelTypeProvider channelTypeProvider) throws IllegalArgumentException { if (bundle == null) { throw new IllegalArgumentException("The Bundle must not be null!"); } if (configDescriptionProvider == null) { throw new IllegalArgumentException("The XmlConfigDescriptionProvider must not be null!"); } if (thingTypeProvider == null) { throw new IllegalArgumentException("The XmlThingTypeProvider must not be null!"); } this.bundle = bundle; this.configDescriptionProvider = configDescriptionProvider; this.thingTypeProvider = thingTypeProvider; this.channelTypeProvider = channelTypeProvider; this.thingTypeRefs = new ArrayList<>(10); this.channelGroupTypeRefs = new ArrayList<>(10); this.channelGroupTypeRefs = new ArrayList<>(10); this.channelTypeRefs = new ArrayList<>(10); } @Override public synchronized void addingObject(List<?> types) { if (types != null) { for (Object type : types) { if (type instanceof ThingTypeXmlResult) { ThingTypeXmlResult typeResult = (ThingTypeXmlResult) type; addConfigDescription(typeResult.getConfigDescription()); this.thingTypeRefs.add(typeResult); } else if (type instanceof ChannelGroupTypeXmlResult) { ChannelGroupTypeXmlResult typeResult = (ChannelGroupTypeXmlResult) type; this.channelGroupTypeRefs.add(typeResult); } else if (type instanceof ChannelTypeXmlResult) { ChannelTypeXmlResult typeResult = (ChannelTypeXmlResult) type; this.channelTypeRefs.add(typeResult); addConfigDescription(typeResult.getConfigDescription()); } else { throw new ConversionException("Unknown data type for '" + type + "'!"); } } } } private void addConfigDescription(ConfigDescription configDescription) { if (configDescription != null) { try { this.configDescriptionProvider.addConfigDescription(this.bundle, configDescription); } catch (Exception ex) { this.logger.error("Could not register ConfigDescription!", ex); } } } @Override public synchronized void addingFinished() { Map<String, ChannelType> channelTypes = new HashMap<>(10); // create channel types for (ChannelTypeXmlResult type : this.channelTypeRefs) { ChannelType channelType = type.toChannelType(); channelTypes.put(channelType.getUID().getAsString(), channelType); this.channelTypeProvider.addChannelType(this.bundle, channelType); } // create channel group types for (ChannelGroupTypeXmlResult type : this.channelGroupTypeRefs) { this.channelTypeProvider.addChannelGroupType(this.bundle, type.toChannelGroupType()); } // create thing and bridge types for (ThingTypeXmlResult type : this.thingTypeRefs) { this.thingTypeProvider.addThingType(this.bundle, type.toThingType()); } // release temporary cache this.thingTypeRefs.clear(); this.channelGroupTypeRefs.clear(); this.channelTypeRefs.clear(); } @Override public synchronized void release() { this.thingTypeProvider.removeAllThingTypes(this.bundle); this.channelTypeProvider.removeAllChannelGroupTypes(this.bundle); this.channelTypeProvider.removeAllChannelTypes(this.bundle); this.configDescriptionProvider.removeAllConfigDescriptions(this.bundle); } }