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