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 */
23/*
24 * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
25 */
26/*
27 * $Id: DOMXMLObject.java 1333415 2012-05-03 12:03:51Z coheigea $
28 */
29package org.jcp.xml.dsig.internal.dom;
30
31import javax.xml.crypto.*;
32import javax.xml.crypto.dom.DOMCryptoContext;
33import javax.xml.crypto.dsig.*;
34
35import java.security.Provider;
36import java.util.*;
37
38import org.w3c.dom.Attr;
39import org.w3c.dom.Document;
40import org.w3c.dom.Element;
41import org.w3c.dom.Node;
42import org.w3c.dom.NodeList;
43
44/**
45 * DOM-based implementation of XMLObject.
46 *
47 * @author Sean Mullan
48 */
49public final class DOMXMLObject extends DOMStructure implements XMLObject {
50
51    private final String id;
52    private final String mimeType;
53    private final String encoding;
54    private final List<XMLStructure> content;
55    private Element objectElem;
56
57    /**
58     * Creates an <code>XMLObject</code> from the specified parameters.
59     *
60     * @param content a list of {@link XMLStructure}s. The list
61     *    is defensively copied to protect against subsequent modification.
62     *    May be <code>null</code> or empty.
63     * @param id the Id (may be <code>null</code>)
64     * @param mimeType the mime type (may be <code>null</code>)
65     * @param encoding the encoding (may be <code>null</code>)
66     * @throws ClassCastException if <code>content</code> contains any
67     *    entries that are not of type {@link XMLStructure}
68     */
69    public DOMXMLObject(List<? extends XMLStructure> content, String id,
70                        String mimeType, String encoding)
71    {
72        List<XMLStructure> tempList =
73            Collections.checkedList(new ArrayList<XMLStructure>(),
74                                    XMLStructure.class);
75        if (content != null) {
76            tempList.addAll(content);
77        }
78        this.content = Collections.unmodifiableList(tempList);
79        this.id = id;
80        this.mimeType = mimeType;
81        this.encoding = encoding;
82    }
83
84    /**
85     * Creates an <code>XMLObject</code> from an element.
86     *
87     * @param objElem an Object element
88     * @throws MarshalException if there is an error when unmarshalling
89     */
90    public DOMXMLObject(Element objElem, XMLCryptoContext context,
91                        Provider provider)
92    throws MarshalException
93    {
94        // unmarshal attributes
95        this.encoding = DOMUtils.getAttributeValue(objElem, "Encoding");
96
97        Attr attr = objElem.getAttributeNodeNS(null, "Id");
98        if (attr != null) {
99            this.id = attr.getValue();
100            objElem.setIdAttributeNode(attr, true);
101        } else {
102            this.id = null;
103        }
104        this.mimeType = DOMUtils.getAttributeValue(objElem, "MimeType");
105
106        NodeList nodes = objElem.getChildNodes();
107        int length = nodes.getLength();
108        List<XMLStructure> content = new ArrayList<XMLStructure>(length);
109        for (int i = 0; i < length; i++) {
110            Node child = nodes.item(i);
111            if (child.getNodeType() == Node.ELEMENT_NODE) {
112                Element childElem = (Element)child;
113                String tag = childElem.getLocalName();
114                if (tag.equals("Manifest")) {
115                    content.add(new DOMManifest(childElem, context, provider));
116                    continue;
117                } else if (tag.equals("SignatureProperties")) {
118                    content.add(new DOMSignatureProperties(childElem, context));
119                    continue;
120                } else if (tag.equals("X509Data")) {
121                    content.add(new DOMX509Data(childElem));
122                    continue;
123                }
124                //@@@FIXME: check for other dsig structures
125            }
126            content.add(new javax.xml.crypto.dom.DOMStructure(child));
127        }
128        if (content.isEmpty()) {
129            this.content = Collections.emptyList();
130        } else {
131            this.content = Collections.unmodifiableList(content);
132        }
133        this.objectElem = objElem;
134    }
135
136    public List<XMLStructure> getContent() {
137        return content;
138    }
139
140    public String getId() {
141        return id;
142    }
143
144    public String getMimeType() {
145        return mimeType;
146    }
147
148    public String getEncoding() {
149        return encoding;
150    }
151
152    public void marshal(Node parent, String dsPrefix, DOMCryptoContext context)
153        throws MarshalException {
154        Document ownerDoc = DOMUtils.getOwnerDocument(parent);
155
156        Element objElem = objectElem != null ? objectElem : null;
157        if (objElem == null) {
158            objElem = DOMUtils.createElement(ownerDoc, "Object",
159                                             XMLSignature.XMLNS, dsPrefix);
160
161            // set attributes
162            DOMUtils.setAttributeID(objElem, "Id", id);
163            DOMUtils.setAttribute(objElem, "MimeType", mimeType);
164            DOMUtils.setAttribute(objElem, "Encoding", encoding);
165
166            // create and append any elements and mixed content, if necessary
167            for (XMLStructure object : content) {
168                if (object instanceof DOMStructure) {
169                    ((DOMStructure)object).marshal(objElem, dsPrefix, context);
170                } else {
171                    javax.xml.crypto.dom.DOMStructure domObject =
172                        (javax.xml.crypto.dom.DOMStructure)object;
173                    DOMUtils.appendChild(objElem, domObject.getNode());
174                }
175            }
176        }
177
178        parent.appendChild(objElem);
179    }
180
181    @Override
182    public boolean equals(Object o) {
183        if (this == o) {
184            return true;
185        }
186
187        if (!(o instanceof XMLObject)) {
188            return false;
189        }
190        XMLObject oxo = (XMLObject)o;
191
192        boolean idsEqual = (id == null ? oxo.getId() == null
193                                       : id.equals(oxo.getId()));
194        boolean encodingsEqual =
195            (encoding == null ? oxo.getEncoding() == null
196                              : encoding.equals(oxo.getEncoding()));
197        boolean mimeTypesEqual =
198            (mimeType == null ? oxo.getMimeType() == null
199                              : mimeType.equals(oxo.getMimeType()));
200
201        List<XMLStructure> oxoContent = oxo.getContent();
202        return (idsEqual && encodingsEqual && mimeTypesEqual &&
203                equalsContent(oxoContent));
204    }
205
206    @Override
207    public int hashCode() {
208        int result = 17;
209        if (id != null) {
210            result = 31 * result + id.hashCode();
211        }
212        if (encoding != null) {
213            result = 31 * result + encoding.hashCode();
214        }
215        if (mimeType != null) {
216            result = 31 * result + mimeType.hashCode();
217        }
218        result = 31 * result + content.hashCode();
219
220        return result;
221    }
222
223    private boolean equalsContent(List<XMLStructure> otherContent) {
224        if (content.size() != otherContent.size()) {
225            return false;
226        }
227        for (int i = 0, osize = otherContent.size(); i < osize; i++) {
228            XMLStructure oxs = otherContent.get(i);
229            XMLStructure xs = content.get(i);
230            if (oxs instanceof javax.xml.crypto.dom.DOMStructure) {
231                if (!(xs instanceof javax.xml.crypto.dom.DOMStructure)) {
232                    return false;
233                }
234                Node onode = ((javax.xml.crypto.dom.DOMStructure)oxs).getNode();
235                Node node = ((javax.xml.crypto.dom.DOMStructure)xs).getNode();
236                if (!DOMUtils.nodesEqual(node, onode)) {
237                    return false;
238                }
239            } else {
240                if (!(xs.equals(oxs))) {
241                    return false;
242                }
243            }
244        }
245
246        return true;
247    }
248}
249