[Markups]
[aprog.git] / Markups / src / net / sourceforge / aprog / markups / MarkupsTools.java
blobc2d57ee10cb3c9a8259a8e9a352017303ab2a5b1
1 /*
2 * The MIT License
3 *
4 * Copyright 2010 Codist Monk.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
25 package net.sourceforge.aprog.markups;
27 import static net.sourceforge.aprog.tools.Tools.debugPrint;
28 import static net.sourceforge.aprog.tools.Tools.ignore;
29 import static net.sourceforge.aprog.tools.Tools.set;
31 import java.awt.BorderLayout;
32 import java.awt.Color;
33 import java.awt.Component;
34 import java.io.ByteArrayOutputStream;
35 import java.util.ArrayList;
36 import java.util.List;
38 import javax.swing.BorderFactory;
39 import javax.swing.JPanel;
40 import javax.swing.event.DocumentEvent;
41 import javax.swing.text.JTextComponent;
43 import net.sourceforge.aprog.context.Context;
44 import net.sourceforge.aprog.events.Variable;
45 import net.sourceforge.aprog.events.Variable.ValueChangedEvent;
46 import net.sourceforge.aprog.i18n.Messages;
47 import net.sourceforge.aprog.markups.MarkupsComponents.AbstractDocumentHandler;
48 import net.sourceforge.aprog.tools.IllegalInstantiationException;
49 import net.sourceforge.aprog.xml.XMLTools;
51 import org.w3c.dom.NamedNodeMap;
52 import org.w3c.dom.Node;
53 import org.w3c.dom.NodeList;
54 import org.w3c.dom.events.EventListener;
56 /**
58 * @author codistmonk (creation 2010-07-04)
60 public final class MarkupsTools {
62 /**
63 * @throws IllegalInstantiationException To prevent instantiation
65 private MarkupsTools() {
66 throw new IllegalInstantiationException();
69 /**
71 * @param translationKey
72 * <br>Not null
73 * @param component
74 * <br>Not null
75 * <br>Input-output
76 * @return
77 * <br>Not null
78 * <br>New
80 public static final JPanel newTitledPanel(final String translationKey, final Component component) {
81 final JPanel result = new JPanel(new BorderLayout());
83 result.setBorder(Messages.translate(BorderFactory.createTitledBorder(translationKey)));
85 result.add(component);
87 return result;
90 /**
92 * @param context
93 * <br>Not null
94 * <br>Input-output
95 * @param variableName
96 * <br>Not null
97 * <br>Shared
98 * @param textComponent
99 * <br>Not null
100 * <br>Shared
101 * <br>Input-output
103 public static final void updateVariableOnTextChanged(final Context context, final String variableName,
104 final JTextComponent textComponent) {
105 textComponent.getDocument().addDocumentListener(new AbstractDocumentHandler() {
107 @Override
108 protected final void eventReceived(final DocumentEvent event) {
109 context.set(variableName, textComponent.getText());
117 * @param context
118 * <br>Not null
119 * <br>Input-output
120 * @param errorVariableName
121 * <br>Not null
122 * <br>Shared
123 * @param component
124 * <br>Not null
125 * <br>Shared
127 public static final void highlightBackgroundOnError(final Context context, final String errorVariableName,
128 final Component component) {
129 addListener(context, errorVariableName, new Variable.Listener<Object>() {
131 private Color defaultBackground;
133 @Override
134 public final void valueChanged(final ValueChangedEvent<Object, ?> event) {
135 if (this.defaultBackground == null) {
136 this.defaultBackground = component.getBackground();
139 component.setBackground(event.getNewValue() == null ? this.defaultBackground : Color.RED);
147 * @param <T> The variable value type
148 * @param context
149 * <br>Not null
150 * <br>Input-output
151 * @param variableName
152 * <br>Not null
153 * @param listener
154 * <br>Not null
155 * <br>Shared
157 public static final <T> void addListener(final Context context, final String variableName,
158 final Variable.Listener<T> listener) {
159 final Variable<T> variable = context.getVariable(variableName);
161 variable.addListener(listener);
166 * @param node
167 * <br>Not null
168 * @return
169 * <br>Not null
170 * <br>New
172 public static final String toXMLString(final Node node) {
173 final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
175 XMLTools.write(node, buffer, 0);
177 return buffer.toString();
182 * @param node
183 * <br>Maybe null
184 * @return
185 * <br>Not null
187 public static final String getIdentifyingXPath(final Node node) {
188 if (node == null || node.getNodeType() == Node.DOCUMENT_NODE) {
189 return "/";
192 if (set(Node.DOCUMENT_FRAGMENT_NODE, Node.ENTITY_NODE, Node.ENTITY_REFERENCE_NODE,
193 Node.NOTATION_NODE, Node.CDATA_SECTION_NODE, Node.PROCESSING_INSTRUCTION_NODE).contains(node.getNodeType())) {
194 return "";
197 final String selector = getXPathSelector(node);
199 debugPrint("../" + selector);
200 debugPrint(node.getParentNode());
202 final Node parent = node.getParentNode() != null ? node.getParentNode() : XMLTools.getNode(node, "..");
203 final StringBuilder pathSelector = new StringBuilder(getIdentifyingXPath(parent));
205 if (!pathSelector.toString().endsWith("/")) {
206 pathSelector.append("/");
209 if (set(Node.ATTRIBUTE_NODE).contains(node.getNodeType())) {
210 return pathSelector + selector;
213 final NodeList siblings = XMLTools.getNodeList(parent, selector);
215 return pathSelector + selector + "[" + (indexOf(siblings, node) + 1) + "]";
219 * Returns a XPath expression that can be used to selects nodes like {@code node}.
221 * @param node
222 * <br>Not null
223 * @return
224 * <br>Not null
226 public static final String getXPathSelector(final Node node) {
227 if (node.getNodeName().startsWith("#")) {
228 return node.getNodeName().toLowerCase().substring(1) + "()";
231 if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
232 return "@" + node.getNodeName();
235 return node.getNodeName();
240 * @param nodes
241 * <br>Not null
242 * @param node
243 * <br>Maybe null
244 * @return {@code -1} if {@code nodes} doesn't contain {@code node}
245 * <br>Range: {@code [-1 .. nodes.getLength() - 1]}
247 public static final int indexOf(final NodeList nodes, final Node node) {
248 for (int i = 0; i < nodes.getLength(); ++i) {
249 if (nodes.item(i) == node) {
250 return i;
254 return -1;
259 * @param node
260 * <br>Not null
261 * @return
262 * <br>Not null
263 * <br>New
265 public static final List<Node> getAttributeChildren(final Node node) {
266 final List<Node> result = new ArrayList<Node>();
267 final NamedNodeMap attributes = node.getAttributes();
269 if (attributes != null) {
270 for (int i = 0; i < attributes.getLength(); ++i) {
271 result.add(attributes.item(i));
275 return result;
280 * @param node
281 * <br>Not null
282 * @return
283 * <br>Not null
284 * <br>New
286 public static final List<Node> getNonattributeChildren(final Node node) {
287 final List<Node> result = new ArrayList<Node>();
289 if (node.getNodeType() != Node.ATTRIBUTE_NODE) {
290 for (final Node domChild : XMLTools.toList(node.getChildNodes())) {
291 result.add(domChild);
295 return result;
300 * @param node
301 * <br>Not null
302 * @return
303 * <br>Not null
304 * <br>New
306 public static final List<Node> getChildren(final Node node) {
307 final List<Node> result = new ArrayList<Node>();
309 result.addAll(getAttributeChildren(node));
310 result.addAll(getNonattributeChildren(node));
312 return result;
319 * @author codistmonk (creation 2010-07-07)
321 abstract class AbstractDOMListenerReattacher implements Variable.Listener<Node> {
323 private final EventListener domListener;
327 * @param domListener
328 * <br>Not null
329 * <br>Shared
331 AbstractDOMListenerReattacher(final EventListener domListener) {
332 this.domListener = domListener;
337 * @return
338 * <br>Not null
339 * <br>Shared
341 public final EventListener getDOMListener() {
342 return this.domListener;
345 @Override
346 public final void valueChanged(final ValueChangedEvent<Node, ?> event) {
347 if (event.getOldValue() != null) {
348 XMLTools.removeDOMEventListener(event.getOldValue(), this.getDOMListener());
351 final Node dom = event.getNewValue();
353 if (dom != null) {
354 XMLTools.addDOMEventListener(dom, this.getDOMListener());
359 * The default implementation does nothing.
361 * @param event
362 * <br>Not null
364 protected void afterReattachment(final ValueChangedEvent<Node, ?> event) {
365 ignore(event);