1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.fourthline.cling.support.lastchange;
17
18 import static org.fourthline.cling.model.XMLUtil.appendNewElement;
19
20 import java.io.InputStream;
21 import java.io.StringReader;
22 import java.lang.reflect.Constructor;
23 import java.util.Collections;
24 import java.util.Map;
25 import java.util.Set;
26 import java.util.logging.Level;
27 import java.util.logging.Logger;
28
29 import javax.xml.parsers.DocumentBuilderFactory;
30
31 import org.fourthline.cling.model.XMLUtil;
32 import org.fourthline.cling.model.types.UnsignedIntegerFourBytes;
33 import org.fourthline.cling.support.shared.AbstractMap;
34 import org.seamless.util.io.IO;
35 import org.seamless.util.Exceptions;
36 import org.seamless.xml.DOMParser;
37 import org.seamless.xml.SAXParser;
38 import org.w3c.dom.Document;
39 import org.w3c.dom.Element;
40 import org.xml.sax.Attributes;
41 import org.xml.sax.InputSource;
42 import org.xml.sax.SAXException;
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public abstract class LastChangeParser extends SAXParser {
59
60 final private static Logger log = Logger.getLogger(LastChangeParser.class.getName());
61
62 public enum CONSTANTS {
63 Event,
64 InstanceID,
65 val;
66
67 public boolean equals(String s) {
68 return this.name().equals(s);
69 }
70 }
71
72 abstract protected String getNamespace();
73
74 protected Set<Class<? extends EventedValue>> getEventedVariables() {
75 return Collections.EMPTY_SET;
76 }
77
78 protected EventedValue createValue(String name, Map.Entry<String, String>[] attributes) throws Exception {
79 for (Class<? extends EventedValue> evType : getEventedVariables()) {
80 if (evType.getSimpleName().equals(name)) {
81 Constructor<? extends EventedValue> ctor = evType.getConstructor(Map.Entry[].class);
82 return ctor.newInstance(new Object[]{attributes});
83 }
84 }
85 return null;
86 }
87
88
89
90
91
92
93
94
95 public Event parseResource(String resource) throws Exception {
96 InputStream is = null;
97 try {
98 is = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
99 return parse(IO.readLines(is));
100 } finally {
101 if (is != null) is.close();
102 }
103 }
104
105 public Event parse(String xml) throws Exception {
106
107 if (xml == null || xml.length() == 0) {
108 throw new RuntimeException("Null or empty XML");
109 }
110
111 Event event = new Event();
112 new RootHandler(event, this);
113
114 if (log.isLoggable(Level.FINE)) {
115 log.fine("Parsing 'LastChange' event XML content");
116 log.fine("===================================== 'LastChange' BEGIN ============================================");
117 log.fine(xml);
118 log.fine("====================================== 'LastChange' END ============================================");
119 }
120 parse(new InputSource(new StringReader(xml)));
121
122 log.fine("Parsed event with instances IDs: " + event.getInstanceIDs().size());
123 if (log.isLoggable(Level.FINEST)) {
124 for (InstanceID instanceID : event.getInstanceIDs()) {
125 log.finest("InstanceID '" + instanceID.getId() + "' has values: " + instanceID.getValues().size());
126 for (EventedValue eventedValue : instanceID.getValues()) {
127 log.finest(eventedValue.getName() + " => " + eventedValue.getValue());
128 }
129 }
130 }
131
132 return event;
133 }
134
135 class RootHandler extends SAXParser.Handler<Event> {
136
137 RootHandler(Event instance, SAXParser parser) {
138 super(instance, parser);
139 }
140
141 RootHandler(Event instance) {
142 super(instance);
143 }
144
145 @Override
146 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
147 super.startElement(uri, localName, qName, attributes);
148 if (CONSTANTS.InstanceID.equals(localName)) {
149 String valAttr = attributes.getValue(CONSTANTS.val.name());
150 if (valAttr != null) {
151 InstanceID instanceID = new InstanceID(new UnsignedIntegerFourBytes(valAttr));
152 getInstance().getInstanceIDs().add(instanceID);
153 new InstanceIDHandler(instanceID, this);
154 }
155 }
156 }
157 }
158
159 class InstanceIDHandler extends SAXParser.Handler<InstanceID> {
160
161 InstanceIDHandler(InstanceID instance, SAXParser.Handler parent) {
162 super(instance, parent);
163 }
164
165 @Override
166 public void startElement(String uri, String localName, String qName, final Attributes attributes) throws SAXException {
167 super.startElement(uri, localName, qName, attributes);
168 Map.Entry[] attributeMap = new Map.Entry[attributes.getLength()];
169 for (int i = 0; i < attributeMap.length; i++) {
170 attributeMap[i] =
171 new AbstractMap.SimpleEntry<>(
172 attributes.getLocalName(i),
173 attributes.getValue(i)
174 );
175 }
176 try {
177 EventedValue esv = createValue(localName, attributeMap);
178 if (esv != null)
179 getInstance().getValues().add(esv);
180 } catch (Exception ex) {
181
182 log.warning("Error reading event XML, ignoring value: " + Exceptions.unwrap(ex));
183 }
184 }
185
186 @Override
187 protected boolean isLastElement(String uri, String localName, String qName) {
188 return CONSTANTS.InstanceID.equals(localName);
189 }
190 }
191
192 public String generate(Event event) throws Exception {
193 return XMLUtil.documentToFragmentString(buildDOM(event));
194 }
195
196 protected Document buildDOM(Event event) throws Exception {
197
198 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
199 factory.setNamespaceAware(true);
200
201 Document d = factory.newDocumentBuilder().newDocument();
202 generateRoot(event, d);
203
204 return d;
205 }
206
207 protected void generateRoot(Event event, Document descriptor) {
208 Element eventElement = descriptor.createElementNS(getNamespace(), CONSTANTS.Event.name());
209 descriptor.appendChild(eventElement);
210 generateInstanceIDs(event, descriptor, eventElement);
211 }
212
213 protected void generateInstanceIDs(Event event, Document descriptor, Element rootElement) {
214 for (InstanceID instanceID : event.getInstanceIDs()) {
215 if (instanceID.getId() == null) continue;
216 Element instanceIDElement = appendNewElement(descriptor, rootElement, CONSTANTS.InstanceID.name());
217 instanceIDElement.setAttribute(CONSTANTS.val.name(), instanceID.getId().toString());
218
219 for (EventedValue eventedValue : instanceID.getValues()) {
220 generateEventedValue(eventedValue, descriptor, instanceIDElement);
221 }
222 }
223 }
224
225 protected void generateEventedValue(EventedValue eventedValue, Document descriptor, Element parentElement) {
226 String name = eventedValue.getName();
227 Map.Entry<String, String>[] attributes = eventedValue.getAttributes();
228 if (attributes != null && attributes.length > 0) {
229 Element evElement = appendNewElement(descriptor, parentElement, name);
230 for (Map.Entry<String, String> attr : attributes) {
231 evElement.setAttribute(attr.getKey(), DOMParser.escape(attr.getValue()));
232 }
233 }
234 }
235
236 }