View Javadoc

1   /* This file is part of COPAL (COntext Provisioning for All).
2    *
3    * COPAL is a part of SM4All (Smart hoMes for All) project.
4    *
5    * COPAL is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU Lesser General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * COPAL is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General Public License
16   * along with COPAL. If not, see <http://www.gnu.org/licenses/>.
17   */
18  package at.ac.tuwien.infosys.sm4all.copal.api.event.xml.type;
19  
20  import java.net.MalformedURLException;
21  import java.net.URL;
22  import java.text.MessageFormat;
23  import org.apache.log4j.Logger;
24  import org.w3c.dom.Document;
25  import org.w3c.dom.Element;
26  import at.ac.tuwien.infosys.sm4all.copal.api.event.ContextEventType;
27  import at.ac.tuwien.infosys.sm4all.copal.api.event.xml.XMLContextEventType;
28  import at.ac.tuwien.infosys.sm4all.copal.api.xml.Constants;
29  import at.ac.tuwien.infosys.sm4all.copal.api.xml.ElementUtil;
30  import at.ac.tuwien.infosys.sm4all.copal.api.xml.InvalidFieldValueException;
31  import at.ac.tuwien.infosys.sm4all.copal.api.xml.Marshaller;
32  import at.ac.tuwien.infosys.sm4all.copal.api.xml.MissingFieldException;
33  import at.ac.tuwien.infosys.sm4all.copal.api.xml.ParsingException;
34  import at.ac.tuwien.infosys.sm4all.copal.api.xml.Unmarshaller;
35  
36  /**
37   * Command that marshalls and unmarshalls schema {@link URL} of a
38   * {@link ContextEventType} into/from an {@link Element}.
39   * 
40   * @author sanjin
41   */
42  public class SchemaURL extends ElementUtil implements Unmarshaller<URL>,
43          Marshaller<XMLContextEventType> {
44  
45      private static final String URL_ELEMENT_NAME = "URL";
46      private static final String CLASSPATH_ELEMENT_NAME = "ClassPath";
47  
48      private static final Logger LOGGER = Logger.getLogger(SchemaURL.class);
49  
50      private final ClassLoader classLoader;
51      private final Element element;
52  
53      /**
54       * Creates instance of {@link ContextEventType} schema {@link URL}
55       * unmarshaller which uses specified {@link Element} to unmarshal the schema
56       * {@link URL}.
57       * 
58       * @param element the {@link Element} used for marshalling and
59       *        unmarshalling.
60       * @throws NullPointerException if specified {@link ClassLoader} or
61       *         {@link Element} is <code>null</code>.
62       */
63      public SchemaURL(final Element element) {
64          this(null, element);
65      }
66  
67      /**
68       * Creates instance of {@link ContextEventType} schema {@link URL}
69       * unmarshaller which uses specified {@link Element} to unmarshal the schema
70       * {@link URL}. The specified {@link ClassLoader} is used for resolving
71       * {@link URL} for a schema that is in the classpath.
72       * 
73       * @param classLoader the {@link ClassLoader} used for resolving {@link URL}
74       *        for a schema that is in the classpath.
75       * @param element the {@link Element} used for marshalling and
76       *        unmarshalling.
77       * @throws NullPointerException if specified {@link Element} is
78       *         <code>null</code>.
79       */
80      public SchemaURL(final ClassLoader classLoader, final Element element) {
81          super(element);
82  
83          this.classLoader = classLoader;
84          this.element = element;
85      }
86  
87      /**
88       * Returns the <code>address</code> attribute of <code>URL</code> child
89       * {@link Element} of specified {@link Element}. Additionally, if the non-
90       * <code>null</code> {@link ClassLoader} in the
91       * {@link SchemaURL#SchemaURL(ClassLoader, Element)} constructor was used,
92       * the <code>ClassPath</code> child {@link Element} will be permitted and
93       * the <code>location</code> attribute will be used to find the resource in
94       * the classpath of specified {@link ClassLoader}.
95       * 
96       * @return the <code>address</code> or <code>location</code> attribute.
97       * @throws MissingFieldException if specified {@link Element} does not have
98       *         a <code>URL</code> or <code>ClassPath</code> child
99       *         {@link Element}, the <code>URL</code> child {@link Element} does
100      *         not have an <code>address</code> attribute, or the
101      *         <code>ClassPath</code> child {@link Element} does not have a
102      *         <code>location</code> attribute.
103      * @throws InvalidFieldValueException if specified {@link Element} has a
104      *         non-<code>URL</code> or <code>ClassPath</code> child
105      *         {@link Element}, the <code>address</code> attribute is empty or
106      *         blank or is malformed {@link URL}, or the <code>location</code>
107      *         attribute is empty or blank or there is no such resource in the
108      *         classpath.
109      */
110     @Override
111     public URL unmarshal() throws ParsingException {
112         Element url = null;
113         try {
114             url = getChildElement(Constants.COPAL_NAMESPACE_URI,
115                     URL_ELEMENT_NAME);
116         } catch (final MissingFieldException ex) {
117             if (LOGGER.isDebugEnabled())
118                 LOGGER.debug("Schema URL element is missing! Ignoring.", ex);
119         }
120         Element classpath = null;
121         try {
122             classpath = getChildElement(Constants.COPAL_NAMESPACE_URI,
123                     CLASSPATH_ELEMENT_NAME);
124         } catch (final MissingFieldException ex) {
125             if (LOGGER.isDebugEnabled())
126                 LOGGER.debug("Schema classpath element is missing! Ignoring.",
127                         ex);
128         }
129 
130         if ((url != null) && (classpath != null))
131             throw new InvalidFieldValueException(MessageFormat.format(
132                     "Multiple schema locations: ''{0}'' and ''{1}''.",
133                     url.getLocalName(), classpath.getLocalName()));
134 
135         URL result = null;
136 
137         if (url != null) {
138             if (!url.hasAttribute("address"))
139                 throw new MissingFieldException("Schema URL address");
140             final String address = url.getAttribute("address").trim();
141             if (address.isEmpty())
142                 throw new InvalidFieldValueException(
143                         "Schema URL address is empty or blank.");
144             try {
145                 result = new URL(url.getAttribute("address"));
146             } catch (final MalformedURLException ex) {
147                 throw new InvalidFieldValueException(
148                         "Schema URL address is malformed.", ex);
149             }
150         }
151         if (classpath != null) {
152             if (this.classLoader == null)
153                 throw new InvalidFieldValueException(
154                         "Schema ClassPath is not permitted.");
155             if (!classpath.hasAttribute("location"))
156                 throw new MissingFieldException("Schema ClassPath location");
157             final String location = classpath.getAttribute("location").trim();
158             if (location.isEmpty())
159                 throw new InvalidFieldValueException(
160                         "Schema ClassPath location is empty or blank.");
161             result = this.classLoader.getResource(location);
162             if (result == null)
163                 throw new InvalidFieldValueException(
164                         "Schema ClassPath location is not in classpath.");
165         }
166 
167         return result;
168     }
169 
170     /**
171      * Possibly removes the <code>ClassPath</code> child {@link Element} of
172      * specified {@link Element} if there is one and removes the
173      * <code>URL</code> child {@link Element} of specified {@link Element} if
174      * specified {@link XMLContextEventType} does not have schema; otherwise the
175      * <code>address</code> attribute of the <code>URL</code> child
176      * {@link Element} is set to specified {@link XMLContextEventType}'s schema.
177      * 
178      * @param eventType the {@link XMLContextEventType}.
179      * @throws NullPointerException if specified {@link XMLContextEventType} is
180      *         <code>null</code>.
181      * @throws IllegalArgumentException if specified {@link XMLContextEventType}
182      *         has no schema.
183      */
184     @Override
185     public void marshal(final XMLContextEventType eventType) {
186         if (eventType == null)
187             throw new NullPointerException("Event type cannot be null.");
188 
189         try {
190             this.element.removeChild(getChildElement(
191                     Constants.COPAL_NAMESPACE_URI, CLASSPATH_ELEMENT_NAME));
192         } catch (final MissingFieldException ex) {
193             if (LOGGER.isDebugEnabled())
194                 LOGGER.debug("Schema classpath element is missing! Ignoring.",
195                         ex);
196         }
197 
198         final Document document = this.element.getOwnerDocument();
199 
200         if (eventType.hasSchema()) {
201             Element url = null;
202             try {
203                 url = getChildElement(Constants.COPAL_NAMESPACE_URI,
204                         URL_ELEMENT_NAME);
205             } catch (final MissingFieldException ex) {
206                 if (LOGGER.isDebugEnabled())
207                     LOGGER.debug(
208                             "Schema URL element is missing! Appending new one.",
209                             ex);
210                 url = document.createElementNS(Constants.COPAL_NAMESPACE_URI,
211                         Constants.COPAL_PREFIX + ":" + URL_ELEMENT_NAME);
212                 this.element.appendChild(url);
213             }
214             url.setAttribute("address", eventType.getSchemaURL().toString());
215         } else
216             try {
217                 this.element.removeChild(getChildElement(
218                         Constants.COPAL_NAMESPACE_URI, URL_ELEMENT_NAME));
219             } catch (final MissingFieldException ex) {
220                 if (LOGGER.isDebugEnabled())
221                     LOGGER.debug("Schema URL element is missing! Ignoring.", ex);
222             }
223     }
224 }