4 * Copyright 2010 Codist Monk.
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
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
;
58 * @author codistmonk (creation 2010-07-04)
60 public final class MarkupsTools
{
63 * @throws IllegalInstantiationException To prevent instantiation
65 private MarkupsTools() {
66 throw new IllegalInstantiationException();
71 * @param translationKey
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
);
98 * @param textComponent
103 public static final void updateVariableOnTextChanged(final Context context
, final String variableName
,
104 final JTextComponent textComponent
) {
105 textComponent
.getDocument().addDocumentListener(new AbstractDocumentHandler() {
108 protected final void eventReceived(final DocumentEvent event
) {
109 context
.set(variableName
, textComponent
.getText());
120 * @param errorVariableName
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
;
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
151 * @param variableName
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
);
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();
187 public static final String
getIdentifyingXPath(final Node node
) {
188 if (node
== null || node
.getNodeType() == Node
.DOCUMENT_NODE
) {
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())) {
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}.
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();
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
) {
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
));
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
);
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
));
319 * @author codistmonk (creation 2010-07-07)
321 abstract class AbstractDOMListenerReattacher
implements Variable
.Listener
<Node
> {
323 private final EventListener domListener
;
331 AbstractDOMListenerReattacher(final EventListener domListener
) {
332 this.domListener
= domListener
;
341 public final EventListener
getDOMListener() {
342 return this.domListener
;
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();
354 XMLTools
.addDOMEventListener(dom
, this.getDOMListener());
359 * The default implementation does nothing.
364 protected void afterReattachment(final ValueChangedEvent
<Node
, ?
> event
) {