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.xml;
19  
20  import java.text.MessageFormat;
21  import java.util.concurrent.atomic.AtomicReference;
22  import org.apache.log4j.Logger;
23  import org.w3c.dom.Element;
24  import org.w3c.dom.Node;
25  import org.w3c.dom.NodeList;
26  import at.ac.tuwien.infosys.sm4all.copal.api.util.Marshaller;
27  import at.ac.tuwien.infosys.sm4all.copal.api.util.MissingFieldException;
28  import static at.ac.tuwien.infosys.sm4all.copal.api.xml.Constants.COPAL_NAMESPACE;
29  
30  /**
31   * Marshals a child {@link Element} of a parent {@link Element}.
32   * 
33   * @param <T> the type of marshaled value.
34   * @author sanjin
35   */
36  public class ElementMarshaller<T> extends Base implements Marshaller<T> {
37  
38      private static final Logger LOGGER = Logger.getLogger(ElementMarshaller.class);
39  
40      private final Element parent;
41      private final String childName;
42      private final MarshallerBuilder<T> builder;
43      private final RemoveStrategy strategy;
44  
45      /**
46       * Creates instance of child {@link Element} {@link Marshaller} of specified
47       * parent {@link Element}. Child {@link Element} will have specified local
48       * name and will be in {@link Constants#COPAL_NAMESPACE}. Specified
49       * {@link MarshallerBuilder} is used to create a {@link Marshaller} that
50       * will marshal a value into the child {@link Element} and specified
51       * {@link RemoveStrategy} is used to remove marshaled value.
52       * 
53       * @param parent the parent {@link Element}.
54       * @param childName the local name of the child {@link Element}.
55       * @param builder the {@link MarshallerBuilder} to create a
56       *        {@link Marshaller} for the child {@link Element}.
57       * @param strategy the {@link RemoveStrategy}.
58       * @throws NullPointerException if specified parent {@link Element}, name of
59       *         child {@link Element}, {@link MarshallerBuilder} or
60       *         {@link RemoveStrategy} is <code>null</code>.
61       * @throws IllegalArgumentException if specified name of child
62       *         {@link Element} is an empty or blank string.
63       */
64      public ElementMarshaller(final Element parent, final String childName,
65              final MarshallerBuilder<T> builder, final RemoveStrategy strategy) {
66          super(parent);
67  
68          if (null == childName) {
69              throw new NullPointerException("Child name cannot be null.");
70          }
71          if (childName.trim().isEmpty()) {
72              throw new IllegalArgumentException(
73                      "Child name cannot be an empty or blank string.");
74          }
75          if (null == builder) {
76              throw new NullPointerException("Marshaller builder cannot be null.");
77          }
78          if (null == strategy) {
79              throw new NullPointerException("Remove strategy cannot be null.");
80          }
81  
82          this.parent = parent;
83          this.childName = childName;
84          this.builder = builder;
85          this.strategy = strategy;
86      }
87  
88      /**
89       * Returns the parent {@link Element}.
90       * 
91       * @return the parent {@link Element}.
92       */
93      public Element getParent() {
94          return this.parent;
95      }
96  
97      /**
98       * Returns the local name of the child {@link Element}.
99       * 
100      * @return the local name of the child {@link Element}.
101      */
102     public String getChildName() {
103         return this.childName;
104     }
105 
106     /**
107      * Returns the {@link MarshallerBuilder} to create a {@link Marshaller} for
108      * the child {@link Element}.
109      * 
110      * @return the {@link MarshallerBuilder} to create a {@link Marshaller} for
111      *         the child {@link Element}.
112      */
113     public MarshallerBuilder<T> getBuilder() {
114         return this.builder;
115     }
116 
117     /**
118      * Returns the {@link RemoveStrategy} used to remove marshaled value.
119      * 
120      * @return the {@link RemoveStrategy} used to remove marshaled value.
121      */
122     public RemoveStrategy getStrategy() {
123         return this.strategy;
124     }
125 
126     /**
127      * Marshals specified value into the child {@link Element} using a
128      * {@link Marshaller} created with the {@link MarshallerBuilder}. If child
129      * {@link Element} is missing in the parent {@link Element}, a new child
130      * {@link Element} will be created and appended.
131      * 
132      * @param value the value to be marshaled into the child {@link Element}.
133      */
134     @Override
135     public void marshal(final T value) {
136         Element child = null;
137         try {
138             child = getChildElement();
139         } catch (final MissingFieldException ex) {
140             if (LOGGER.isDebugEnabled()) {
141                 LOGGER.debug(MessageFormat.format(
142                         "''{0}'' element is missing! Appending new one.",
143                         this.childName), ex);
144             }
145             child = createElement(this.childName);
146             this.parent.appendChild(child);
147         }
148         this.builder.withElement(child).build().marshal(value);
149     }
150 
151     /**
152      * Removes the child {@link Element}.
153      */
154     @Override
155     public void remove() {
156         try {
157             switch (this.strategy) {
158             case RemoveElement:
159                 this.parent.removeChild(getChildElement());
160                 break;
161             case Delegate:
162                 this.builder.withElement(getChildElement()).build().remove();
163                 break;
164             default:
165                 throw new UnsupportedOperationException(MessageFormat.format(
166                         "Unknown remove strategy: {0}", this.strategy));
167             }
168         } catch (final MissingFieldException ex) {
169             if (LOGGER.isDebugEnabled()) {
170                 LOGGER.debug(MessageFormat.format(
171                         "{0} element is missing! Ignoring.", this.childName),
172                         ex);
173             }
174         }
175     }
176 
177     /**
178      * Returns the first child {@link Element} of the parent {@link Element}.
179      * 
180      * @return first child {@link Element}.
181      * @throws MissingFieldException if there is no such child {@link Element}.
182      */
183     protected Element getChildElement() throws MissingFieldException {
184         Element result = null;
185 
186         final NodeList children = this.parent.getElementsByTagNameNS(
187                 COPAL_NAMESPACE, this.childName);
188         final int length = children.getLength();
189         Node node;
190         for (int i = 0; i < length; i++) {
191             node = children.item(i);
192             if ((this.parent.equals(node.getParentNode()))
193                     && (node instanceof Element)) {
194                 result = (Element) node;
195                 break;
196             }
197         }
198 
199         if (null == result) {
200             throw new MissingFieldException(this.childName);
201         }
202 
203         return result;
204     }
205 
206     /**
207      * Builder of {@link ElementMarshaller}.
208      * 
209      * @param <T> the type of marshaled value.
210      * @author sanjin
211      */
212     public static class Builder<T> extends BaseMarshallerBuilder<T> {
213 
214         private final AtomicReference<String> childNameRef = new AtomicReference<String>();
215         private final AtomicReference<MarshallerBuilder<T>> builderRef = new AtomicReference<MarshallerBuilder<T>>();
216         private final AtomicReference<RemoveStrategy> strategyRef = new AtomicReference<RemoveStrategy>();
217 
218         /**
219          * Create uninitialized instance of {@link ElementMarshaller.Builder}.
220          */
221         public Builder() {
222             super();
223         }
224 
225         /**
226          * Clone-constructor.
227          * 
228          * @param builder the cloned {@link ElementMarshaller.Builder}.
229          */
230         protected Builder(final Builder<T> builder) {
231             super(builder);
232 
233             this.childNameRef.set(builder.childNameRef.get());
234             this.builderRef.set(builder.builderRef.get());
235             this.strategyRef.set(builder.strategyRef.get());
236         }
237 
238         /**
239          * Returns the parent {@link Element}.
240          * 
241          * @return the parent {@link Element}.
242          */
243         @Override
244         public Element getElement() {
245             return super.getElement();
246         }
247 
248         /**
249          * Returns the local name of the child {@link Element}.
250          * 
251          * @return the local name of the child {@link Element}.
252          */
253         public String getChildName() {
254             return this.childNameRef.get();
255         }
256 
257         /**
258          * Returns the {@link MarshallerBuilder} to create a {@link Marshaller}
259          * for the child {@link Element}.
260          * 
261          * @return the {@link MarshallerBuilder} to create a {@link Marshaller}
262          *         for the child {@link Element}.
263          */
264         public MarshallerBuilder<T> getBuilder() {
265             return this.builderRef.get();
266         }
267 
268         /**
269          * Returns the {@link RemoveStrategy} used to remove marshaled value.
270          * 
271          * @return the {@link RemoveStrategy} used to remove marshaled value.
272          */
273         public RemoveStrategy getStrategy() {
274             return this.strategyRef.get();
275         }
276 
277         /**
278          * Create instance of {@link ElementMarshaller.Builder} that will build
279          * {@link ElementMarshaller}s that will marshal values to specified
280          * parent {@link Element}.
281          * 
282          * @param element the parent {@link Element}.
283          * @return an {@link ElementMarshaller.Builder}.
284          */
285         @Override
286         public Builder<T> withElement(final Element element) {
287             return (Builder<T>) super.withElement(element);
288         }
289 
290         /**
291          * Create instance of {@link ElementMarshaller.Builder} that will build
292          * {@link ElementMarshaller}s that will marshal values using specified
293          * local name for the child {@link Element}.
294          * 
295          * @param childName the local name of the child {@link Element}.
296          * @return an {@link ElementMarshaller.Builder}.
297          */
298         public Builder<T> withChildName(final String childName) {
299             final Builder<T> result = copy();
300 
301             result.childNameRef.set(childName);
302 
303             return result;
304         }
305 
306         /**
307          * Create instance of {@link ElementMarshaller.Builder} that will build
308          * {@link ElementMarshaller}s that will marshal values using specified
309          * {@link MarshallerBuilder} to create a {@link Marshaller} for the
310          * child {@link Element}.
311          * 
312          * @param builder the {@link MarshallerBuilder} to create a
313          *        {@link Marshaller} for the child {@link Element}.
314          * @return an {@link ElementMarshaller.Builder}.
315          */
316         public Builder<T> withBuilder(final MarshallerBuilder<T> builder) {
317             final Builder<T> result = copy();
318 
319             result.builderRef.set(builder);
320 
321             return result;
322         }
323 
324         /**
325          * Create instance of {@link ElementMarshaller.Builder} that will build
326          * {@link ElementMarshaller}s that will marshal values using specified
327          * {@link RemoveStrategy}.
328          * 
329          * @param strategy the {@link RemoveStrategy}.
330          * @return an {@link ElementMarshaller.Builder}.
331          */
332         public Builder<T> withStrategy(final RemoveStrategy strategy) {
333             final Builder<T> result = copy();
334 
335             result.strategyRef.set(strategy);
336 
337             return result;
338         }
339 
340         /**
341          * Create instance of {@link ElementMarshaller}.
342          * 
343          * @return a {@link ElementMarshaller}.
344          */
345         @Override
346         public ElementMarshaller<T> build() {
347             return new ElementMarshaller<T>(getElement(), getChildName(),
348                     getBuilder(), getStrategy());
349         }
350 
351         @Override
352         protected Builder<T> copy() {
353             return new Builder<T>(this);
354         }
355     }
356 }