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.util.LinkedList;
21  import java.util.List;
22  import java.util.concurrent.atomic.AtomicReference;
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 static at.ac.tuwien.infosys.sm4all.copal.api.xml.Constants.COPAL_NAMESPACE;
28  
29  /**
30   * Marshals a {@link List} of values to an {@link Element}.
31   * 
32   * @param <T> the type of marshaled {@link List} elements.
33   * @author sanjin
34   */
35  public class ListMarshaller<T> extends Base implements Marshaller<List<T>> {
36  
37      private final Element parent;
38      private final String childName;
39      private final MarshallerBuilder<T> builder;
40  
41      /**
42       * Creates instance of {@link List} {@link Marshaller} that marshals each
43       * value from a {@link List} into a separate child {@link Element} of
44       * specified parent {@link Element}. Child {@link Element}s will have
45       * specified local name and will be in {@link Constants#COPAL_NAMESPACE}.
46       * Specified {@link MarshallerBuilder} is used to create a
47       * {@link Marshaller} that will marshal a value into a child {@link Element}
48       * .
49       * 
50       * @param parent the parent {@link Element}.
51       * @param childName the local name of child {@link Element}s.
52       * @param builder the {@link MarshallerBuilder} to create {@link Marshaller}
53       *        s for child {@link Element}s.
54       * @throws NullPointerException if specified parent {@link Element}, name of
55       *         child {@link Element}s or {@link MarshallerBuilder} is
56       *         <code>null</code>.
57       * @throws IllegalArgumentException if specified name of child
58       *         {@link Element}s is an empty or blank string.
59       */
60      public ListMarshaller(final Element parent, final String childName,
61              final MarshallerBuilder<T> builder) {
62          super(parent);
63  
64          if (null == childName) {
65              throw new NullPointerException("Child name cannot be null.");
66          }
67          if (childName.trim().isEmpty()) {
68              throw new IllegalArgumentException(
69                      "Child name cannot be an empty or blank string.");
70          }
71          if (null == builder) {
72              throw new NullPointerException("Marshaller builder cannot be null.");
73          }
74  
75          this.parent = parent;
76          this.childName = childName;
77          this.builder = builder;
78      }
79  
80      /**
81       * Returns the parent {@link Element}.
82       * 
83       * @return the parent {@link Element}.
84       */
85      public Element getParent() {
86          return this.parent;
87      }
88  
89      /**
90       * Returns the local name of the child {@link Element}s.
91       * 
92       * @return the local name of the child {@link Element}s.
93       */
94      public String getChildName() {
95          return this.childName;
96      }
97  
98      /**
99       * Returns the {@link MarshallerBuilder} to create a {@link Marshaller}s for
100      * the child {@link Element}s.
101      * 
102      * @return the {@link MarshallerBuilder} to create a {@link Marshaller}s for
103      *         the child {@link Element}s.
104      */
105     public MarshallerBuilder<T> getBuilder() {
106         return this.builder;
107     }
108 
109     /**
110      * Marshals each value in specified {@link List} into a child
111      * {@link Element} using a {@link Marshaller} created with the
112      * {@link MarshallerBuilder}. Child {@link Element}s already in the parent
113      * {@link Element} are reused.
114      * 
115      * @param list the {@link List} of values to be marshaled into the parent
116      *        {@link Element}.
117      * @throws NullPointerException if specified {@link List} is
118      *         <code>null</code>.
119      */
120     @Override
121     public void marshal(final List<T> list) {
122         if (null == list) {
123             throw new NullPointerException("List cannot be null.");
124         }
125 
126         final Element[] children = getChildElements();
127         final int end = Math.min(children.length, list.size());
128 
129         /* marshal into already present child elements */
130         for (int i = 0; i < end; i++) {
131             this.builder.withElement(children[i]).build().marshal(list.get(i));
132         }
133 
134         if (end < children.length) {
135             /*
136              * the parent element has more child elements than needed; remove
137              * unneeded child elements
138              */
139             for (int i = end; i < children.length; i++) {
140                 this.parent.removeChild(children[i]);
141             }
142         } else if (end < list.size()) {
143             /*
144              * the parent element has less child elements than required; add new
145              * child elements
146              */
147             final int size = list.size();
148             Element child;
149             for (int i = end; i < size; i++) {
150                 child = createElement(this.childName);
151                 this.parent.appendChild(child);
152                 this.builder.withElement(child).build().marshal(list.get(i));
153             }
154         }
155     }
156 
157     /**
158      * Removes all child {@link Element}s.
159      */
160     @Override
161     public void remove() {
162         for (final Element child : getChildElements()) {
163             this.parent.removeChild(child);
164         }
165     }
166 
167     /**
168      * Returns all child {@link Element}s.
169      * 
170      * @return the child {@link Element}s.
171      */
172     protected Element[] getChildElements() {
173 
174         final List<Element> result = new LinkedList<Element>();
175 
176         final NodeList inputElements = this.parent.getElementsByTagNameNS(
177                 COPAL_NAMESPACE, this.childName);
178         final int length = inputElements.getLength();
179         Node node;
180         for (int i = 0; i < length; i++) {
181             node = inputElements.item(i);
182             if ((this.parent.equals(node.getParentNode()))
183                     && (node instanceof Element)) {
184                 result.add((Element) node);
185             }
186         }
187 
188         return result.toArray(new Element[result.size()]);
189     }
190 
191     /**
192      * Builder of {@link ListMarshaller}.
193      * 
194      * @param <T> the type of marshaled {@link List} elements.
195      * @author sanjin
196      */
197     public static class Builder<T> extends BaseMarshallerBuilder<List<T>> {
198 
199         private final AtomicReference<String> childNameRef = new AtomicReference<String>();
200         private final AtomicReference<MarshallerBuilder<T>> builderRef = new AtomicReference<MarshallerBuilder<T>>();
201 
202         /**
203          * Create uninitialized instance of {@link ListMarshaller.Builder}.
204          */
205         public Builder() {
206             super();
207         }
208 
209         /**
210          * Clone-constructor.
211          * 
212          * @param builder the cloned {@link ListMarshaller.Builder}.
213          */
214         protected Builder(final Builder<T> builder) {
215             super(builder);
216 
217             this.childNameRef.set(builder.childNameRef.get());
218             this.builderRef.set(builder.builderRef.get());
219         }
220 
221         /**
222          * Returns the parent {@link Element}.
223          * 
224          * @return the parent {@link Element}.
225          */
226         @Override
227         public Element getElement() {
228             return super.getElement();
229         }
230 
231         /**
232          * Returns the local name of child {@link Element}s.
233          * 
234          * @return the local name of child {@link Element}s.
235          */
236         public String getChildName() {
237             return this.childNameRef.get();
238         }
239 
240         /**
241          * Returns the {@link MarshallerBuilder} to create {@link Marshaller}s
242          * for child {@link Element}s.
243          * 
244          * @return the {@link MarshallerBuilder} to create {@link Marshaller}s
245          *         for child {@link Element}s.
246          */
247         public MarshallerBuilder<T> getBuilder() {
248             return this.builderRef.get();
249         }
250 
251         /**
252          * Create instance of {@link ListMarshaller.Builder} that will build
253          * {@link ListMarshaller}s that will marshal values to specified parent
254          * {@link Element}.
255          * 
256          * @param element the parent {@link Element}.
257          * @return a {@link ListMarshaller.Builder}.
258          */
259         @Override
260         public Builder<T> withElement(final Element element) {
261             return (Builder<T>) super.withElement(element);
262         }
263 
264         /**
265          * Create instance of {@link ListMarshaller.Builder} that will build
266          * {@link ListMarshaller}s that will marshal values using specified
267          * local name for the child {@link Element}.
268          * 
269          * @param childName the local name of the child {@link Element}.
270          * @return a {@link ListMarshaller.Builder}.
271          */
272         public Builder<T> withChildName(final String childName) {
273             final Builder<T> result = copy();
274 
275             result.childNameRef.set(childName);
276 
277             return result;
278         }
279 
280         /**
281          * Create instance of {@link ListMarshaller.Builder} that will build
282          * {@link ListMarshaller}s that will marshal values using specified
283          * {@link MarshallerBuilder} to create a {@link Marshaller} for the
284          * child {@link Element}.
285          * 
286          * @param builder the {@link MarshallerBuilder} to create a
287          *        {@link Marshaller} for the child {@link Element}.
288          * @return a {@link ListMarshaller.Builder}.
289          */
290         public Builder<T> withBuilder(final MarshallerBuilder<T> builder) {
291             final Builder<T> result = copy();
292 
293             result.builderRef.set(builder);
294 
295             return result;
296         }
297 
298         /**
299          * Create instance of {@link ListMarshaller}.
300          * 
301          * @return a {@link ListMarshaller}.
302          */
303         @Override
304         public ListMarshaller<T> build() {
305             return new ListMarshaller<T>(getElement(), getChildName(),
306                     getBuilder());
307         }
308 
309         @Override
310         protected Builder<T> copy() {
311             return new Builder<T>(this);
312         }
313     }
314 }