View Javadoc

1   /***
2    *  Copyright 2003-2010 Terracotta, Inc.
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  
17  package net.sf.ehcache.config.generator.model;
18  
19  import java.io.PrintWriter;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  
24  /***
25   *
26   * Implementation of an ElementVisitor extending from {@link AbstractDepthFirstVisitor} which can generate XML out of a {@link NodeElement}.
27   * Accepts a {@link PrintWriter} in the constructor and uses it to output the generated XML.
28   * Output can be controlled by enabling/disabling the various options present in {@link OutputBehavior} by calling
29   * {@link #enableOutputBehavior(OutputBehavior)} or {@link #disableOutputBehavior(OutputBehavior)}
30   *
31   * @author Abhishek Sanoujam
32   *
33   */
34  public class XMLGeneratorVisitor extends AbstractDepthFirstVisitor {
35  
36      /***
37       * Enum controlling the generated XML output
38       *
39       * @author Abhishek Sanoujam
40       *
41       */
42      public static enum OutputBehavior {
43          /***
44           * Output behavior controlling whether child elements should be indented or not
45           */
46          INDENT_CHIlD_ELEMENTS,
47          /***
48           * Output behavior controlling whether new lines should be added for each child element
49           */
50          NEWLINE_FOR_EACH_ELEMENT,
51          /***
52           * Output behavior controlling whether new lines should be added for each attribute
53           */
54          NEWLINE_FOR_EACH_ATTRIBUTE,
55          /***
56           * Output behavior controlling whether optional attributes having default values should be generated or not
57           */
58          OUTPUT_OPTIONAL_ATTRIBUTES_WITH_DEFAULT_VALUES,
59          /***
60           * Output behavior controlling whether new lines should be added at the end or not
61           */
62          NEWLINE_AT_END;
63      }
64  
65      private static final String SPACER = "    ";
66  
67      private final Map<OutputBehavior, Boolean> enabledOutputBehaviors = new HashMap<OutputBehavior, Boolean>();
68      private final PrintWriter out;
69      private int indent;
70      private NodeElement rootElement;
71      private boolean visitedFirstElement;
72  
73      /***
74       * Constructor accepting the {@link PrintWriter}. All output behaviors are enabled by default.
75       *
76       * @param out
77       *            the {@link PrintWriter}
78       */
79      public XMLGeneratorVisitor(PrintWriter out) {
80          this.out = out;
81          enableAllOutputBehaviors();
82      }
83  
84      /***
85       * Enables all output behaviors
86       */
87      public void enableAllOutputBehaviors() {
88          for (OutputBehavior behavior : OutputBehavior.values()) {
89              enableOutputBehavior(behavior);
90          }
91      }
92  
93      /***
94       * Disables all output behaviors
95       */
96      public void disableAllOutputBehaviors() {
97          enabledOutputBehaviors.clear();
98      }
99  
100     /***
101      * Enables one particular {@link OutputBehavior}
102      *
103      * @param behavior
104      */
105     public void enableOutputBehavior(OutputBehavior behavior) {
106         enabledOutputBehaviors.put(behavior, Boolean.TRUE);
107     }
108 
109     /***
110      * Disables one particular {@link OutputBehavior}
111      *
112      * @param behavior
113      */
114     public void disableOutputBehavior(OutputBehavior behavior) {
115         enabledOutputBehaviors.remove(behavior);
116     }
117 
118     /***
119      * Returns true if the output behavior is enabled
120      *
121      * @param behavior
122      *            the output behavior to inspect
123      * @return true if enabled, otherwise false
124      */
125     public boolean isOutputBehaviorEnabled(OutputBehavior behavior) {
126         Boolean enabled = enabledOutputBehaviors.get(behavior);
127         return enabled != null && enabled;
128     }
129 
130     private void print(String string) {
131         out.print(spacer() + string);
132     }
133 
134     private void printWithoutSpacer(String string) {
135         out.print(string);
136     }
137 
138     private void newLine() {
139         out.println(spacer());
140     }
141 
142     private String spacer() {
143         StringBuilder sb = new StringBuilder(SPACER.length() * indent);
144         for (int i = 0; i < indent; i++) {
145             sb.append(SPACER);
146         }
147         return sb.toString();
148     }
149 
150     private void indentForward() {
151         indent++;
152     }
153 
154     private void indentBackward() {
155         indent--;
156     }
157 
158     /***
159      * {@inheritDoc}
160      */
161     @Override
162     protected void startElement(NodeElement element) {
163         if (isOutputBehaviorEnabled(OutputBehavior.NEWLINE_FOR_EACH_ELEMENT) && visitedFirstElement) {
164             newLine();
165         }
166         print("<" + element.getName());
167         if (!visitedFirstElement) {
168             rootElement = element;
169             visitedFirstElement = true;
170         }
171     }
172 
173     /***
174      * {@inheritDoc}
175      */
176     @Override
177     protected void startAttributes(NodeElement element) {
178         if (isOutputBehaviorEnabled(OutputBehavior.NEWLINE_FOR_EACH_ATTRIBUTE)) {
179             indentForward();
180         }
181     }
182 
183     /***
184      * {@inheritDoc}
185      */
186     @Override
187     protected void visitAttributes(NodeElement element, List<NodeAttribute> attributes) {
188         for (NodeAttribute attribute : attributes) {
189             visitAttribute(element, attribute);
190         }
191     }
192 
193     /***
194      * Visits an attribute.
195      *
196      * @param element
197      * @param attribute
198      */
199     protected void visitAttribute(NodeElement element, NodeAttribute attribute) {
200         String value = attribute.getValue();
201         if (!isOutputBehaviorEnabled(OutputBehavior.OUTPUT_OPTIONAL_ATTRIBUTES_WITH_DEFAULT_VALUES)) {
202             if (attribute.isOptional()) {
203                 if (value != null && value.equals(attribute.getDefaultValue())) {
204                     // do not optional attributes with default values, as defined by outputBehaviors
205                     return;
206                 }
207             }
208         }
209         if (value == null) {
210             value = attribute.getDefaultValue();
211         }
212         if (value != null) {
213             printWithoutSpacer(" ");
214             String line = attribute.getName() + "=\"" + value + "\"";
215             if (isOutputBehaviorEnabled(OutputBehavior.NEWLINE_FOR_EACH_ATTRIBUTE)) {
216                 newLine();
217                 print(line);
218             } else {
219                 printWithoutSpacer(line);
220             }
221         }
222 
223     }
224 
225     /***
226      * {@inheritDoc}
227      */
228     @Override
229     protected void endAttributes(NodeElement element) {
230         printWithoutSpacer(">");
231         if (isOutputBehaviorEnabled(OutputBehavior.NEWLINE_FOR_EACH_ATTRIBUTE)) {
232             indentBackward();
233         }
234     }
235 
236     /***
237      * {@inheritDoc}
238      */
239     @Override
240     protected void visitElement(NodeElement element) {
241         if (element.getInnerContent() != null) {
242             indentForward();
243             newLine();
244             print(element.getInnerContent());
245             indentBackward();
246         }
247     }
248 
249     /***
250      * {@inheritDoc}
251      */
252     @Override
253     protected void startChildren(NodeElement element) {
254         if (isOutputBehaviorEnabled(OutputBehavior.INDENT_CHIlD_ELEMENTS)) {
255             indentForward();
256         }
257     }
258 
259     /***
260      * {@inheritDoc}
261      */
262     @Override
263     protected void endChildren(NodeElement element) {
264         if (isOutputBehaviorEnabled(OutputBehavior.INDENT_CHIlD_ELEMENTS)) {
265             indentBackward();
266         }
267     }
268 
269     /***
270      * {@inheritDoc}
271      */
272     @Override
273     protected void endElement(NodeElement element) {
274         if (isOutputBehaviorEnabled(OutputBehavior.NEWLINE_FOR_EACH_ELEMENT)) {
275             newLine();
276         }
277         print("</" + element.getName() + ">");
278         if (element.equals(rootElement) && isOutputBehaviorEnabled(OutputBehavior.NEWLINE_AT_END)) {
279             newLine();
280         }
281     }
282 
283 }