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
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 }