1/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5/**
6 * Licensed to the Apache Software Foundation (ASF) under one
7 * or more contributor license agreements. See the NOTICE file
8 * distributed with this work for additional information
9 * regarding copyright ownership. The ASF licenses this file
10 * to you under the Apache License, Version 2.0 (the
11 * "License"); you may not use this file except in compliance
12 * with the License. You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing,
17 * software distributed under the License is distributed on an
18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19 * KIND, either express or implied. See the License for the
20 * specific language governing permissions and limitations
21 * under the License.
22 */
23package com.sun.org.apache.xml.internal.security.encryption;
24
25import java.io.ByteArrayOutputStream;
26import java.io.IOException;
27import java.io.OutputStreamWriter;
28import java.io.UnsupportedEncodingException;
29import java.util.HashMap;
30import java.util.Map;
31
32import com.sun.org.apache.xml.internal.security.c14n.Canonicalizer;
33import org.w3c.dom.Element;
34import org.w3c.dom.NamedNodeMap;
35import org.w3c.dom.Node;
36import org.w3c.dom.NodeList;
37
38/**
39 * Converts <code>String</code>s into <code>Node</code>s and visa versa.
40 *
41 * An abstract class for common Serializer functionality
42 */
43public abstract class AbstractSerializer implements Serializer {
44
45    protected Canonicalizer canon;
46
47    public void setCanonicalizer(Canonicalizer canon) {
48        this.canon = canon;
49    }
50
51    /**
52     * Returns a <code>String</code> representation of the specified
53     * <code>Element</code>.
54     * <p/>
55     * Refer also to comments about setup of format.
56     *
57     * @param element the <code>Element</code> to serialize.
58     * @return the <code>String</code> representation of the serilaized
59     *   <code>Element</code>.
60     * @throws Exception
61     */
62    public String serialize(Element element) throws Exception {
63        return canonSerialize(element);
64    }
65
66    /**
67     * Returns a <code>byte[]</code> representation of the specified
68     * <code>Element</code>.
69     *
70     * @param element the <code>Element</code> to serialize.
71     * @return the <code>byte[]</code> representation of the serilaized
72     *   <code>Element</code>.
73     * @throws Exception
74     */
75    public byte[] serializeToByteArray(Element element) throws Exception {
76        return canonSerializeToByteArray(element);
77    }
78
79    /**
80     * Returns a <code>String</code> representation of the specified
81     * <code>NodeList</code>.
82     * <p/>
83     * This is a special case because the NodeList may represent a
84     * <code>DocumentFragment</code>. A document fragment may be a
85     * non-valid XML document (refer to appropriate description of
86     * W3C) because it my start with a non-element node, e.g. a text
87     * node.
88     * <p/>
89     * The methods first converts the node list into a document fragment.
90     * Special care is taken to not destroy the current document, thus
91     * the method clones the nodes (deep cloning) before it appends
92     * them to the document fragment.
93     * <p/>
94     * Refer also to comments about setup of format.
95     *
96     * @param content the <code>NodeList</code> to serialize.
97     * @return the <code>String</code> representation of the serialized
98     *   <code>NodeList</code>.
99     * @throws Exception
100     */
101    public String serialize(NodeList content) throws Exception {
102        ByteArrayOutputStream baos = new ByteArrayOutputStream();
103        canon.setWriter(baos);
104        canon.notReset();
105        for (int i = 0; i < content.getLength(); i++) {
106            canon.canonicalizeSubtree(content.item(i));
107        }
108        String ret = baos.toString("UTF-8");
109        baos.reset();
110        return ret;
111    }
112
113    /**
114     * Returns a <code>byte[]</code> representation of the specified
115     * <code>NodeList</code>.
116     *
117     * @param content the <code>NodeList</code> to serialize.
118     * @return the <code>byte[]</code> representation of the serialized
119     *   <code>NodeList</code>.
120     * @throws Exception
121     */
122    public byte[] serializeToByteArray(NodeList content) throws Exception {
123        ByteArrayOutputStream baos = new ByteArrayOutputStream();
124        canon.setWriter(baos);
125        canon.notReset();
126        for (int i = 0; i < content.getLength(); i++) {
127            canon.canonicalizeSubtree(content.item(i));
128        }
129        return baos.toByteArray();
130    }
131
132    /**
133     * Use the Canonicalizer to serialize the node
134     * @param node
135     * @return the canonicalization of the node
136     * @throws Exception
137     */
138    public String canonSerialize(Node node) throws Exception {
139        ByteArrayOutputStream baos = new ByteArrayOutputStream();
140        canon.setWriter(baos);
141        canon.notReset();
142        canon.canonicalizeSubtree(node);
143        String ret = baos.toString("UTF-8");
144        baos.reset();
145        return ret;
146    }
147
148    /**
149     * Use the Canonicalizer to serialize the node
150     * @param node
151     * @return the (byte[]) canonicalization of the node
152     * @throws Exception
153     */
154    public byte[] canonSerializeToByteArray(Node node) throws Exception {
155        ByteArrayOutputStream baos = new ByteArrayOutputStream();
156        canon.setWriter(baos);
157        canon.notReset();
158        canon.canonicalizeSubtree(node);
159        return baos.toByteArray();
160    }
161
162    /**
163     * @param source
164     * @param ctx
165     * @return the Node resulting from the parse of the source
166     * @throws XMLEncryptionException
167     */
168    public abstract Node deserialize(String source, Node ctx) throws XMLEncryptionException;
169
170    /**
171     * @param source
172     * @param ctx
173     * @return the Node resulting from the parse of the source
174     * @throws XMLEncryptionException
175     */
176    public abstract Node deserialize(byte[] source, Node ctx) throws XMLEncryptionException;
177
178    protected static byte[] createContext(byte[] source, Node ctx) throws XMLEncryptionException {
179        // Create the context to parse the document against
180        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
181        try {
182            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(byteArrayOutputStream, "UTF-8");
183            outputStreamWriter.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?><dummy");
184
185            // Run through each node up to the document node and find any xmlns: nodes
186            Map<String, String> storedNamespaces = new HashMap<String, String>();
187            Node wk = ctx;
188            while (wk != null) {
189                NamedNodeMap atts = wk.getAttributes();
190                if (atts != null) {
191                    for (int i = 0; i < atts.getLength(); ++i) {
192                        Node att = atts.item(i);
193                        String nodeName = att.getNodeName();
194                        if ((nodeName.equals("xmlns") || nodeName.startsWith("xmlns:"))
195                                && !storedNamespaces.containsKey(att.getNodeName())) {
196                            outputStreamWriter.write(" ");
197                            outputStreamWriter.write(nodeName);
198                            outputStreamWriter.write("=\"");
199                            outputStreamWriter.write(att.getNodeValue());
200                            outputStreamWriter.write("\"");
201                            storedNamespaces.put(nodeName, att.getNodeValue());
202                        }
203                    }
204                }
205                wk = wk.getParentNode();
206            }
207            outputStreamWriter.write(">");
208            outputStreamWriter.flush();
209            byteArrayOutputStream.write(source);
210
211            outputStreamWriter.write("</dummy>");
212            outputStreamWriter.close();
213
214            return byteArrayOutputStream.toByteArray();
215        } catch (UnsupportedEncodingException e) {
216            throw new XMLEncryptionException("empty", e);
217        } catch (IOException e) {
218            throw new XMLEncryptionException("empty", e);
219        }
220    }
221
222    protected static String createContext(String source, Node ctx) {
223        // Create the context to parse the document against
224        StringBuilder sb = new StringBuilder();
225        sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><dummy");
226
227        // Run through each node up to the document node and find any xmlns: nodes
228        Map<String, String> storedNamespaces = new HashMap<String, String>();
229        Node wk = ctx;
230        while (wk != null) {
231            NamedNodeMap atts = wk.getAttributes();
232            if (atts != null) {
233                for (int i = 0; i < atts.getLength(); ++i) {
234                    Node att = atts.item(i);
235                    String nodeName = att.getNodeName();
236                    if ((nodeName.equals("xmlns") || nodeName.startsWith("xmlns:"))
237                        && !storedNamespaces.containsKey(att.getNodeName())) {
238                        sb.append(' ').append(nodeName).append("=\"")
239                                .append(att.getNodeValue()).append('"');
240                        storedNamespaces.put(nodeName, att.getNodeValue());
241                    }
242                }
243            }
244            wk = wk.getParentNode();
245        }
246        sb.append('>').append(source).append("</dummy>");
247        return sb.toString();
248    }
249
250}
251