/*
* Copyright (c) 2006 Stiftung Deutsches Elektronen-Synchroton,
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
*
* THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS.
* WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND
* NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE
* IN ANY RESPECT, THE USER ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR
* CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
* NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
* DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
* OR MODIFICATIONS.
* THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, MODIFICATION,
* USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS
* PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY
* AT HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM
*/
package org.csstudio.sds.internal.persistence;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.csstudio.platform.simpledal.ConnectionState;
import org.csstudio.sds.internal.model.Layer;
import org.csstudio.sds.internal.rules.ParameterDescriptor;
import org.csstudio.sds.model.AbstractWidgetModel;
import org.csstudio.sds.model.ContainerModel;
import org.csstudio.sds.model.DisplayModel;
import org.csstudio.sds.model.DynamicsDescriptor;
import org.csstudio.sds.model.WidgetProperty;
import org.csstudio.dal.DynamicValueState;
import org.jdom.Element;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
/**
* <code>InputStream</code> that provides "on the fly" access to the XML
* representation of <code>DisplayModels</code>.
*
* @author Alexander Will
* @version $Revision: 1.3 $
*
*/
public final class DisplayModelInputStream extends ByteArrayInputStream {
/**
* The outputter that is used for XML generation.
*/
private XMLOutputter _xmlOutputter;
/**
* The XML document header.
*/
private static String _xmlHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; //$NON-NLS-1$
/**
* The XML document footer.
*/
private static final String XML_FOOTER = "\n</display>"; //$NON-NLS-1$
/**
* The <code>DisplayModel</code>.
*/
private DisplayModel _displayModel;
/**
* The widget models of the <code>DisplayModel</code>.
*/
private List<AbstractWidgetModel> _widgetModels;
/**
* The index of the widget model that currently fills the internal buffer.
*/
private int _elementIndex;
/**
* Flag that indicates whether the model properties have been processed.
*/
private boolean _finishedModelProperties = false;
/**
* Standard constructor.
*
* @param displayModel
* The <code>DisplayModel</code> that should be accessed.
*/
public DisplayModelInputStream(final DisplayModel displayModel) {
super(_xmlHeader.getBytes());
_xmlOutputter = new XMLOutputter(Format.getPrettyFormat());
_displayModel = displayModel;
_widgetModels = _displayModel.getWidgets();
_elementIndex = 0;
_finishedModelProperties = false;
}
/**
* Reset the underlying buffer to the given array of bytes.
*
* @param buffer
* The new state of the internal buffer.
*/
private void resetBuffer(final byte[] buffer) {
this.mark = 0;
this.pos = 0;
this.buf = buffer;
this.count = buf.length;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized int read() {
int result = super.read();
if (available() == 0) {
handleNextElement();
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized int read(final byte[] b, final int off, final int len) {
int result = super.read(b, off, len);
if (available() == 0) {
handleNextElement();
}
return result;
}
/**
* Handle the next element by shifting it into the underlying buffer.
*/
private void handleNextElement() {
if (!_finishedModelProperties) {
resetBuffer(writeModelElementToByteArr(_displayModel));
_finishedModelProperties = true;
} else {
if (_elementIndex < _widgetModels.size()) {
AbstractWidgetModel widgetModel = _widgetModels
.get(_elementIndex);
resetBuffer(writeWidgetToByteArr(widgetModel));
_elementIndex++;
} else if (_elementIndex == _widgetModels.size()) {
resetBuffer(XML_FOOTER.getBytes());
_elementIndex++;
}
}
}
/**
* Return the XML representation of the given widget model as an array of
* bytes.
*
* @param modelElement
* A widget model.
* @return The XML representation of the given widget model as an array of
* bytes.
*/
public byte[] writeWidgetToByteArr(final AbstractWidgetModel modelElement) {
return writeWidgetToString(modelElement).getBytes();
}
/**
* Return the XML representation of the given widget model as a String.
*
* @param modelElement
* A widget model.
* @return The XML representation of the given widget model as a String.
*/
private String writeWidgetToString(final AbstractWidgetModel modelElement) {
Element domModelElement = createModelTag(modelElement);
String outputString = _xmlOutputter.outputString(domModelElement)
+ "\n"; //$NON-NLS-1$
return outputString;
}
/**
* Return the XML representation of the given display model as an array of
* bytes.
*
* @param displayModel
* The display model.
* @return The XML representation of the given display model as an array of
* bytes.
*/
public byte[] writeModelElementToByteArr(final DisplayModel displayModel) {
return writeModelElementToString(displayModel).getBytes();
}
/**
* Return the XML representation of the given display model as an array of
* bytes.
*
* @param displayModel
* The display model.
* @return The XML representation of the given display model as an array of
* bytes.
*/
private String writeModelElementToString(final DisplayModel displayModel) {
Element domDisplayElement = createDisplayTag(displayModel);
String outputString = _xmlOutputter.outputString(domDisplayElement)
.replace("</" + XmlConstants.XML_ELEMENT_DISPLAY + ">", "");//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return outputString;
}
/**
* Create the XML representation of the given display model.
*
* @param displayModel
* The display model.
* @return The XML representation of the given display model.
*/
private Element createDisplayTag(final DisplayModel displayModel) {
Element result = new Element(XmlConstants.XML_ELEMENT_DISPLAY);
result.setAttribute(XmlConstants.XML_ATTRIBUTE_MODEL_VERSION, "1.1"); //$NON-NLS-1$
for (WidgetProperty property : displayModel.getProperties()) {
result.addContent(createPropertyTag(property));
}
for (Layer layer : displayModel.getLayerSupport().getLayers()) {
Element layerTag = createLayerTag(layer.getId(), layer.getDescription(), displayModel
.getLayerSupport().getLayerIndex(layer), layer.isVisible());
result.addContent(layerTag);
}
return result;
}
/**
* Create the XML representation of the given widget model.
*
* @param widgetModel
* A widget model.
* @return The XML representation of the given widget model.
*/
private Element createModelTag(final AbstractWidgetModel widgetModel) {
Element result = new Element(XmlConstants.XML_ELEMENT_WIDGET);
result.setAttribute(XmlConstants.XML_ATTRIBUTE_WIDGET_TYPE, widgetModel
.getTypeID());
Element aliasDescriptorsTag = createAliasDescriptorsTag(widgetModel);
if (aliasDescriptorsTag.getChildren().size() > 0) {
result.addContent(aliasDescriptorsTag);
}
if (widgetModel instanceof ContainerModel) {
ContainerModel container = (ContainerModel) widgetModel;
for (Layer layer : container.getLayerSupport().getLayers()) {
Element layerTag = createLayerTag(layer.getId(), layer.getDescription(), container
.getLayerSupport().getLayerIndex(layer), layer
.isVisible());
result.addContent(layerTag);
}
for (AbstractWidgetModel childModel : container.getWidgets()) {
Element childTag = createModelTag(childModel);
result.addContent(childTag);
}
}
for (WidgetProperty property : widgetModel.getProperties()) {
result.addContent(createPropertyTag(property));
}
return result;
}
/**
* Create a <code>property</code> tag from the given
* <code>WidgetProperty</code>.
*
* @param propertyId
* The ID of the given <code>WidgetProperty</code>.
* @param elementProperty
* An <code>WidgetProperty</code>.
* @return A <code>property</code> XML tag from the given
* <code>WidgetProperty</code>.
*/
private Element createPropertyTag(final WidgetProperty elementProperty) {
Element result = new Element(XmlConstants.XML_ELEMENT_PROPERTY);
result.setAttribute(XmlConstants.XML_ATTRIBUTE_PROPERTY_TYPE,
elementProperty.getPropertyType().toPortableString());
result.setAttribute(XmlConstants.XML_ATTRIBUTE_PROPERTY_ID, elementProperty.getId());
writePropertyValue(elementProperty, result);
Element dynamicsDescriptorTag = createDynamicsDescriptorTag(elementProperty);
if (dynamicsDescriptorTag != null) {
result.addContent(dynamicsDescriptorTag);
}
return result;
}
/**
* Create a <code>layer</code> XML tag from the given
* <code>layerName</code>. <code>layerName</code>.
*
* @param layerName
* The id of the given Layer.
* @param layerName
* The name of the given Layer.
* @param index
* The index of the Layer
* @param isVisible
* Representing if the Layer is visible or not
* @return A <code>layer</code> XML tag from the given
* <code>layerName</code>
*/
private Element createLayerTag(final String layerId, final String layerName, final int index,
final boolean isVisible) {
Element result = new Element(XmlConstants.XML_LAYER);
result.setAttribute(XmlConstants.XML_LAYER_ID, layerId);
result.setAttribute(XmlConstants.XML_LAYER_NAME, layerName);
result.setAttribute(XmlConstants.XML_LAYER_INDEX, String
.valueOf(index));
result.setAttribute(XmlConstants.XML_LAYER_VISIBILITY, String
.valueOf(isVisible));
return result;
}
/**
* Create a <code>dynamicsDescriptor</code> XML tag from the given
* <code>WidgetProperty</code>.
*
* @param elementProperty
* An <code>WidgetProperty</code>.
* @return A <code>dynamicsDescriptor</code> XML tag from the given
* <code>WidgetProperty</code>.
*/
private Element createDynamicsDescriptorTag(
final WidgetProperty elementProperty) {
Element result = null;
DynamicsDescriptor dynamicsDescriptor = elementProperty
.getDynamicsDescriptor();
if (dynamicsDescriptor != null) {
result = new Element(XmlConstants.XML_ELEMENT_DYNAMICS_DESCRIPTOR);
result.setAttribute(XmlConstants.XML_ATTRIBUTE_RULE_ID,
dynamicsDescriptor.getRuleId());
String string = Boolean.toString(dynamicsDescriptor.isUsingOnlyConnectionStates());
result.setAttribute(XmlConstants.XML_ATTRIBUTE_USE_CONNECTION_STATES, string);
// add the input parameter bindings as well!
for (ParameterDescriptor inputChannel : dynamicsDescriptor
.getInputChannels()) {
result.addContent(createParameterElement(
XmlConstants.XML_ELEMENT_INPUT_CHANNEL, inputChannel));
}
// if there is an output parameter, add it as well!
if (dynamicsDescriptor.getOutputChannel() != null) {
result.addContent(createParameterElement(
XmlConstants.XML_ELEMENT_OUTPUT_CHANNEL,
dynamicsDescriptor.getOutputChannel()));
}
AbstractPropertyPersistenceHandler persistenceHandler = PropertyPersistenceHandlerRegistry
.getInstance().getPersistenceHandler(
elementProperty.getPropertyType());
// add values for connection states
Map<ConnectionState, Object> connectionStateValues = dynamicsDescriptor
.getConnectionStateDependentPropertyValues();
if (connectionStateValues != null) {
List<ConnectionState> connectionStates = new ArrayList<ConnectionState>(connectionStateValues.keySet());
Collections.sort(connectionStates);
for (ConnectionState connectionState : connectionStates) {
Object value = connectionStateValues.get(connectionState);
if (persistenceHandler != null) {
Element connectionStateTag = new Element(
XmlConstants.XML_ELEMENT_CONNECTION_STATE);
connectionStateTag.setAttribute(
XmlConstants.XML_ATTRIBUTE_STATE,
connectionState.name());
persistenceHandler.writeProperty(connectionStateTag,
value);
result.addContent(connectionStateTag);
}
}
}
// add values for dynamic value states
Map<DynamicValueState, Object> dynamicValueStateValues = dynamicsDescriptor
.getConditionStateDependentPropertyValues();
if (dynamicValueStateValues != null) {
for (DynamicValueState dynamicValueState : dynamicValueStateValues
.keySet()) {
Object value = dynamicValueStateValues
.get(dynamicValueState);
if (persistenceHandler != null) {
Element conditionStateTag = new Element(
XmlConstants.XML_ELEMENT_DYNAMIC_VALUE_STATE);
conditionStateTag.setAttribute(
XmlConstants.XML_ATTRIBUTE_STATE,
dynamicValueState.name());
persistenceHandler.writeProperty(conditionStateTag,
value);
result.addContent(conditionStateTag);
}
}
}
}
return result;
}
/**
* Create a parameter element.
*
* @param elementName
* The element name (for the input or output parameter tag).
* @param parameter
* The parameter descriptor.
* @return A parameter element.
*/
private Element createParameterElement(final String elementName,
final ParameterDescriptor parameter) {
Element result = new Element(elementName);
result.setAttribute(XmlConstants.XML_ATTRIBUTE_CHANNEL_NAME, parameter
.getChannel());
result.setAttribute(XmlConstants.XML_ATTRIBUTE_CHANNEL_VALUE, parameter.getValue());
return result;
}
/**
* Write the given widget property to the given XML property tag. In case of
* a simple property, a "value" attribute is added. Complex properties might
* also add child tags.
*
* @param elementProperty
* An <code>WidgetProperty</code>.
* @param propertyTag
* The XML property tag the property should be written to.
*/
private void writePropertyValue(final WidgetProperty elementProperty,
final Element propertyTag) {
AbstractPropertyPersistenceHandler persistenceHandler = PropertyPersistenceHandlerRegistry
.getInstance().getPersistenceHandler(
elementProperty.getPropertyType());
if (persistenceHandler != null) {
persistenceHandler.writeProperty(propertyTag, elementProperty
.getPropertyValue());
}
}
/**
* Create a <code>aliasDescriptors</code> XML tag from the given display
* widget model.
*
* @param modelElement
* A widget model.
* @return A <code>aliasDescriptors</code> XML tag from the given display
* widget model.
*/
private Element createAliasDescriptorsTag(
final AbstractWidgetModel modelElement) {
Element result = null;
Map<String, String> aliases = modelElement.getAliases();
result = new Element(XmlConstants.XML_ELEMENT_ALIAS_DESCRIPTORS);
for (String key : aliases.keySet()) {
Element aliasElement = new Element(XmlConstants.XML_ELEMENT_ALIAS);
aliasElement.setAttribute(XmlConstants.XML_ATTRIBUTE_ALIAS_NAME,
key);
aliasElement.setAttribute(XmlConstants.XML_ATTRIBUTE_ALIAS_VALUE,
aliases.get(key));
// aliasElement.setAttribute(
// XmlConstants.XML_ATTRIBUTE_ALIAS_DESCRIPTION, descriptor
// .getDescription());
result.addContent(aliasElement);
}
return result;
}
public String getAsString() {
StringBuffer buffer = new StringBuffer();
buffer.append(this.writeModelElementToString(_displayModel));
for (AbstractWidgetModel model : _widgetModels) {
buffer.append(this.writeWidgetToString(model));
}
return buffer.toString();
}
/**
* Set the XML Header for the Stream. The default is "<?xml version=\"1.0\"
* encoding=\"UTF-8\"?>\n"
*
* @param header
* The XML Header.
*/
public static void setXMLHeader(final String header) {
_xmlHeader = header;
}
}