001    package net.sourceforge.jeuclid.test.content;
002    
003    import java.util.HashMap;
004    import java.util.LinkedHashSet;
005    import java.util.Map;
006    import java.util.Set;
007    
008    import javax.annotation.Nonnull;
009    import javax.annotation.Nullable;
010    import javax.xml.xpath.XPath;
011    import javax.xml.xpath.XPathConstants;
012    import javax.xml.xpath.XPathExpression;
013    import javax.xml.xpath.XPathFactory;
014    
015    import junit.framework.Assert;
016    import net.sourceforge.jeuclid.DOMBuilder;
017    import net.sourceforge.jeuclid.MathMLParserSupport;
018    import net.sourceforge.jeuclid.MathMLSerializer;
019    import net.sourceforge.jeuclid.elements.AbstractJEuclidElement;
020    import net.sourceforge.jeuclid.elements.support.NamespaceContextAdder;
021    
022    import org.junit.Ignore;
023    import org.junit.Test;
024    import org.w3c.dom.Document;
025    import org.w3c.dom.Element;
026    import org.w3c.dom.NamedNodeMap;
027    import org.w3c.dom.Node;
028    import org.w3c.dom.NodeList;
029    
030    public class ContentTransformationTest {
031    
032        private void testbyXpath(final String mathWithContent,
033                final String... tests) throws Exception {
034    
035            final XPathFactory factory = XPathFactory.newInstance();
036            final XPath xpath = factory.newXPath();
037            xpath.setNamespaceContext(new NamespaceContextAdder("m",
038                    AbstractJEuclidElement.URI, null));
039    
040            final Document dorig = MathMLParserSupport.parseString("<math>"
041                    + mathWithContent + "</math>");
042            final Document d = DOMBuilder.getInstance().createJeuclidDom(dorig,
043                    true, true);
044    
045            final int count = tests.length / 2;
046            for (int i = 0; i < count; i++) {
047                final String expr = tests[i * 2];
048                final String text = tests[i * 2 + 1];
049    
050                final XPathExpression xpathExpr = xpath.compile("/m:math" + expr);
051                final Node n = (Node) xpathExpr.evaluate(d, XPathConstants.NODE);
052                Assert.assertNotNull(n);
053                Assert.assertEquals(text, n.getTextContent());
054            }
055        }
056    
057        public void testByDirectComparison(final String contentMath,
058                final String presentationMath) throws Exception {
059            final Document dorigcontent = MathMLParserSupport.parseString("<math>"
060                    + contentMath + "</math>");
061            final Document dcontent = DOMBuilder.getInstance().createJeuclidDom(
062                    dorigcontent, true, true);
063            final Document dorigpresentation = MathMLParserSupport
064                    .parseString("<math>" + presentationMath + "</math>");
065            final Document dpresentation = DOMBuilder.getInstance()
066                    .createJeuclidDom(dorigpresentation, true, true);
067    
068            this.removeDoubleNS(dcontent, null);
069            this.removeDoubleNS(dpresentation, null);
070    
071            final String cString = MathMLSerializer.serializeDocument(dcontent,
072                    false, true);
073            final String pString = MathMLSerializer.serializeDocument(
074                    dpresentation, false, true);
075    
076            Assert.assertEquals(pString, cString);
077        }
078    
079        private void removeDoubleNS(@Nonnull final Node node,
080                @Nullable final Map<String, Set<String>> namespaces) {
081            if (node.getNodeType() == Node.TEXT_NODE) {
082                return;
083            }
084            final Map<String, Set<String>> contextHere = new HashMap<String, Set<String>>();
085            if (namespaces != null) {
086                contextHere.putAll(namespaces);
087            }
088            final NamedNodeMap allAttrs = node.getAttributes();
089            if (allAttrs != null) {
090                for (int i = 0; i < allAttrs.getLength(); i++) {
091                    final Node attr = allAttrs.item(i);
092                    if (attr.getNodeName().startsWith("xmlns")) {
093                        String prefix = attr.getLocalName();
094                        final String namespace = attr.getNodeValue();
095                        if ("xmlns".equals(prefix)) {
096                            prefix = "";
097                        }
098    
099                        final Set<String> currentlyKnown = contextHere
100                                .get(namespace);
101                        final Set<String> newSet = new LinkedHashSet<String>();
102                        if (currentlyKnown != null) {
103                            newSet.addAll(currentlyKnown);
104                        }
105                        if (!newSet.isEmpty()) {
106                            ((Element) node).removeAttribute(attr.getNodeName());
107                            ((Element) node).removeAttributeNS(
108                                    attr.getNamespaceURI(), attr.getLocalName());
109                        }
110                        newSet.add(prefix);
111                        contextHere.put(namespace, newSet);
112                    }
113                }
114            }
115    
116            final String name = node.getNodeName();
117            String prefix;
118            final String[] elements = name.split(":");
119            if (elements.length == 1) {
120                prefix = "";
121            } else {
122                prefix = elements[0];
123            }
124    
125            for (final Map.Entry<String, Set<String>> nsEntry : contextHere
126                    .entrySet()) {
127                final Set<String> prefixes = nsEntry.getValue();
128    
129                if (prefixes.contains(prefix)) {
130                    final String first = prefixes.iterator().next();
131                    if (!first.equals(prefix)) {
132                        final StringBuilder newName = new StringBuilder(first);
133                        if (first.length()!=0) {
134                            newName.append(':');
135                        }
136                        newName.append(node.getLocalName());
137                        ((Element) node).setPrefix(first);
138                    }
139                }
140            }
141    
142    
143            final NodeList children = node.getChildNodes();
144            if (children != null) {
145                for (int i = 0; i < children.getLength(); i++) {
146                    this.removeDoubleNS(children.item(i), contextHere);
147                }
148            }
149        }
150    
151        @Test
152        public void testSimpleTransformations() throws Exception {
153            this.testbyXpath("<apply><plus/><cn>5</cn><ci>x</ci></apply>",
154                    "/m:mrow/m:mn[1]", "5", "/m:mrow/m:mo[1]", "+",
155                    "/m:mrow/m:mi[1]", "x");
156            this.testbyXpath("<apply><times/><cn>5</cn><ci>x</ci></apply>",
157                    "/m:mrow/m:mn[1]", "5", "/m:mrow/m:mo[1]", "\u2062",
158                    "/m:mrow/m:mi[1]", "x");
159        }
160    
161        @Test
162        public void testSimpleTransformations2() throws Exception {
163            this.testByDirectComparison(
164                    "<apply><plus/><cn>5</cn><ci>x</ci></apply>",
165                    "<mrow><mn>5</mn><mo>+</mo><mi>x</mi></mrow>");
166            this.testByDirectComparison(
167                    "<apply><times/><cn>5</cn><ci>x</ci></apply>",
168                    "<mrow><mn>5</mn><mo>&#x2062;</mo><mi>x</mi></mrow>");
169        }
170    
171        // Not yet functional.
172        @Ignore
173        @Test
174        public void testContentParam() throws Exception {
175            this.testbyXpath("<apply><times/><cn>5</cn><ci>x</ci></apply>",
176                    "/m:mrow/m:mn[1]", "5", "/m:mrow/m:mo[1]", "\u00d7",
177                    "/m:mrow/m:mi[1]", "x");
178        }
179    }