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  * ===========================================================================
  28  *
  29  * (C) Copyright IBM Corp. 2003 All Rights Reserved.
  30  *
  31  * ===========================================================================
  32  */
  33 /*
  34  * $Id: DOMReference.java 1334007 2012-05-04 14:59:46Z coheigea $
  35  */
  36 package org.jcp.xml.dsig.internal.dom;
  37 
  38 import javax.xml.crypto.*;
  39 import javax.xml.crypto.dsig.*;
  40 import javax.xml.crypto.dom.DOMCryptoContext;
  41 import javax.xml.crypto.dom.DOMURIReference;
  42 
  43 import java.io.*;
  44 import java.net.URI;
  45 import java.net.URISyntaxException;
  46 import java.security.*;
  47 import java.util.*;
  48 import org.w3c.dom.Attr;
  49 import org.w3c.dom.Document;
  50 import org.w3c.dom.Element;
  51 import org.w3c.dom.Node;
  52 
  53 import org.jcp.xml.dsig.internal.DigesterOutputStream;
  54 import com.sun.org.apache.xml.internal.security.algorithms.MessageDigestAlgorithm;
  55 import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
  56 import com.sun.org.apache.xml.internal.security.signature.XMLSignatureInput;
  57 import com.sun.org.apache.xml.internal.security.utils.Base64;
  58 import com.sun.org.apache.xml.internal.security.utils.UnsyncBufferedOutputStream;
  59 
  60 /**
  61  * DOM-based implementation of Reference.
  62  *
  63  * @author Sean Mullan
  64  * @author Joyce Leung
  65  */
  66 public final class DOMReference extends DOMStructure
  67     implements Reference, DOMURIReference {
  68 
  69    /**
  70     * The maximum number of transforms per reference, if secure validation is enabled.
  71     */
  72    public static final int MAXIMUM_TRANSFORM_COUNT = 5;
  73 
  74    /**
  75     * Look up useC14N11 system property. If true, an explicit C14N11 transform
  76     * will be added if necessary when generating the signature. See section
  77     * 3.1.1 of http://www.w3.org/2007/xmlsec/Drafts/xmldsig-core/ for more info.
  78     *
  79     * If true, overrides the same property if set in the XMLSignContext.
  80     */
  81     private static boolean useC14N11 =
  82         AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
  83             public Boolean run() {
  84                 return Boolean.valueOf(Boolean.getBoolean
  85                     ("com.sun.org.apache.xml.internal.security.useC14N11"));
  86             }
  87         });
  88 
  89     private static java.util.logging.Logger log =
  90         java.util.logging.Logger.getLogger("org.jcp.xml.dsig.internal.dom");
  91 
  92     private final DigestMethod digestMethod;
  93     private final String id;
  94     private final List<Transform> transforms;
  95     private List<Transform> allTransforms;
  96     private final Data appliedTransformData;
  97     private Attr here;
  98     private final String uri;
  99     private final String type;
 100     private byte[] digestValue;
 101     private byte[] calcDigestValue;
 102     private Element refElem;
 103     private boolean digested = false;
 104     private boolean validated = false;
 105     private boolean validationStatus;
 106     private Data derefData;
 107     private InputStream dis;
 108     private MessageDigest md;
 109     private Provider provider;
 110 
 111     /**
 112      * Creates a <code>Reference</code> from the specified parameters.
 113      *
 114      * @param uri the URI (may be null)
 115      * @param type the type (may be null)
 116      * @param dm the digest method
 117      * @param transforms a list of {@link Transform}s. The list
 118      *    is defensively copied to protect against subsequent modification.
 119      *    May be <code>null</code> or empty.
 120      * @param id the reference ID (may be <code>null</code>)
 121      * @return a <code>Reference</code>
 122      * @throws NullPointerException if <code>dm</code> is <code>null</code>
 123      * @throws ClassCastException if any of the <code>transforms</code> are
 124      *    not of type <code>Transform</code>
 125      */
 126     public DOMReference(String uri, String type, DigestMethod dm,
 127                         List<? extends Transform> transforms, String id,
 128                         Provider provider)
 129     {
 130         this(uri, type, dm, null, null, transforms, id, null, provider);
 131     }
 132 
 133     public DOMReference(String uri, String type, DigestMethod dm,
 134                         List<? extends Transform> appliedTransforms,
 135                         Data result, List<? extends Transform> transforms,
 136                         String id, Provider provider)
 137     {
 138         this(uri, type, dm, appliedTransforms,
 139              result, transforms, id, null, provider);
 140     }
 141 
 142     public DOMReference(String uri, String type, DigestMethod dm,
 143                         List<? extends Transform> appliedTransforms,
 144                         Data result, List<? extends Transform> transforms,
 145                         String id, byte[] digestValue, Provider provider)
 146     {
 147         if (dm == null) {
 148             throw new NullPointerException("DigestMethod must be non-null");
 149         }
 150         if (appliedTransforms == null) {
 151             this.allTransforms = new ArrayList<Transform>();
 152         } else {
 153             this.allTransforms = new ArrayList<Transform>(appliedTransforms);
 154             for (int i = 0, size = this.allTransforms.size(); i < size; i++) {
 155                 if (!(this.allTransforms.get(i) instanceof Transform)) {
 156                     throw new ClassCastException
 157                         ("appliedTransforms["+i+"] is not a valid type");
 158                 }
 159             }
 160         }
 161         if (transforms == null) {
 162             this.transforms = Collections.emptyList();
 163         } else {
 164             this.transforms = new ArrayList<Transform>(transforms);
 165             for (int i = 0, size = this.transforms.size(); i < size; i++) {
 166                 if (!(this.transforms.get(i) instanceof Transform)) {
 167                     throw new ClassCastException
 168                         ("transforms["+i+"] is not a valid type");
 169                 }
 170             }
 171             this.allTransforms.addAll(this.transforms);
 172         }
 173         this.digestMethod = dm;
 174         this.uri = uri;
 175         if ((uri != null) && (!uri.equals(""))) {
 176             try {
 177                 new URI(uri);
 178             } catch (URISyntaxException e) {
 179                 throw new IllegalArgumentException(e.getMessage());
 180             }
 181         }
 182         this.type = type;
 183         this.id = id;
 184         if (digestValue != null) {
 185             this.digestValue = digestValue.clone();
 186             this.digested = true;
 187         }
 188         this.appliedTransformData = result;
 189         this.provider = provider;
 190     }
 191 
 192     /**
 193      * Creates a <code>DOMReference</code> from an element.
 194      *
 195      * @param refElem a Reference element
 196      */
 197     public DOMReference(Element refElem, XMLCryptoContext context,
 198                         Provider provider)
 199         throws MarshalException
 200     {
 201         boolean secVal = Utils.secureValidation(context);
 202 
 203         // unmarshal Transforms, if specified
 204         Element nextSibling = DOMUtils.getFirstChildElement(refElem);
 205         List<Transform> transforms = new ArrayList<Transform>(5);
 206         if (nextSibling.getLocalName().equals("Transforms")) {
 207             Element transformElem = DOMUtils.getFirstChildElement(nextSibling,
 208                                                                   "Transform");
 209             transforms.add(new DOMTransform(transformElem, context, provider));
 210             transformElem = DOMUtils.getNextSiblingElement(transformElem);
 211             while (transformElem != null) {
 212                 String localName = transformElem.getLocalName();
 213                 if (!localName.equals("Transform")) {
 214                     throw new MarshalException(
 215                         "Invalid element name: " + localName +
 216                         ", expected Transform");
 217                 }
 218                 transforms.add
 219                     (new DOMTransform(transformElem, context, provider));
 220                 if (secVal && (transforms.size() > MAXIMUM_TRANSFORM_COUNT)) {
 221                     String error = "A maxiumum of " + MAXIMUM_TRANSFORM_COUNT + " "
 222                         + "transforms per Reference are allowed with secure validation";
 223                     throw new MarshalException(error);
 224                 }
 225                 transformElem = DOMUtils.getNextSiblingElement(transformElem);
 226             }
 227             nextSibling = DOMUtils.getNextSiblingElement(nextSibling);
 228         }
 229         if (!nextSibling.getLocalName().equals("DigestMethod")) {
 230             throw new MarshalException("Invalid element name: " +
 231                                        nextSibling.getLocalName() +
 232                                        ", expected DigestMethod");
 233         }
 234 
 235         // unmarshal DigestMethod
 236         Element dmElem = nextSibling;
 237         this.digestMethod = DOMDigestMethod.unmarshal(dmElem);
 238         String digestMethodAlgorithm = this.digestMethod.getAlgorithm();
 239         if (secVal
 240             && MessageDigestAlgorithm.ALGO_ID_DIGEST_NOT_RECOMMENDED_MD5.equals(digestMethodAlgorithm)) {
 241             throw new MarshalException(
 242                 "It is forbidden to use algorithm " + digestMethod + " when secure validation is enabled"
 243             );
 244         }
 245 
 246         // unmarshal DigestValue
 247         Element dvElem = DOMUtils.getNextSiblingElement(dmElem, "DigestValue");
 248         try {
 249             this.digestValue = Base64.decode(dvElem);
 250         } catch (Base64DecodingException bde) {
 251             throw new MarshalException(bde);
 252         }
 253 
 254         // check for extra elements
 255         if (DOMUtils.getNextSiblingElement(dvElem) != null) {
 256             throw new MarshalException(
 257                 "Unexpected element after DigestValue element");
 258         }
 259 
 260         // unmarshal attributes
 261         this.uri = DOMUtils.getAttributeValue(refElem, "URI");
 262 
 263         Attr attr = refElem.getAttributeNodeNS(null, "Id");
 264         if (attr != null) {
 265             this.id = attr.getValue();
 266             refElem.setIdAttributeNode(attr, true);
 267         } else {
 268             this.id = null;
 269         }
 270 
 271         this.type = DOMUtils.getAttributeValue(refElem, "Type");
 272         this.here = refElem.getAttributeNodeNS(null, "URI");
 273         this.refElem = refElem;
 274         this.transforms = transforms;
 275         this.allTransforms = transforms;
 276         this.appliedTransformData = null;
 277         this.provider = provider;
 278     }
 279 
 280     public DigestMethod getDigestMethod() {
 281         return digestMethod;
 282     }
 283 
 284     public String getId() {
 285         return id;
 286     }
 287 
 288     public String getURI() {
 289         return uri;
 290     }
 291 
 292     public String getType() {
 293         return type;
 294     }
 295 
 296     public List<Transform> getTransforms() {
 297         return Collections.unmodifiableList(allTransforms);
 298     }
 299 
 300     public byte[] getDigestValue() {
 301         return (digestValue == null ? null : digestValue.clone());
 302     }
 303 
 304     public byte[] getCalculatedDigestValue() {
 305         return (calcDigestValue == null ? null
 306                                         : calcDigestValue.clone());
 307     }
 308 
 309     public void marshal(Node parent, String dsPrefix, DOMCryptoContext context)
 310         throws MarshalException
 311     {
 312         if (log.isLoggable(java.util.logging.Level.FINE)) {
 313             log.log(java.util.logging.Level.FINE, "Marshalling Reference");
 314         }
 315         Document ownerDoc = DOMUtils.getOwnerDocument(parent);
 316 
 317         refElem = DOMUtils.createElement(ownerDoc, "Reference",
 318                                          XMLSignature.XMLNS, dsPrefix);
 319 
 320         // set attributes
 321         DOMUtils.setAttributeID(refElem, "Id", id);
 322         DOMUtils.setAttribute(refElem, "URI", uri);
 323         DOMUtils.setAttribute(refElem, "Type", type);
 324 
 325         // create and append Transforms element
 326         if (!allTransforms.isEmpty()) {
 327             Element transformsElem = DOMUtils.createElement(ownerDoc,
 328                                                             "Transforms",
 329                                                             XMLSignature.XMLNS,
 330                                                             dsPrefix);
 331             refElem.appendChild(transformsElem);
 332             for (Transform transform : allTransforms) {
 333                 ((DOMStructure)transform).marshal(transformsElem,
 334                                                   dsPrefix, context);
 335             }
 336         }
 337 
 338         // create and append DigestMethod element
 339         ((DOMDigestMethod)digestMethod).marshal(refElem, dsPrefix, context);
 340 
 341         // create and append DigestValue element
 342         if (log.isLoggable(java.util.logging.Level.FINE)) {
 343             log.log(java.util.logging.Level.FINE, "Adding digestValueElem");
 344         }
 345         Element digestValueElem = DOMUtils.createElement(ownerDoc,
 346                                                          "DigestValue",
 347                                                          XMLSignature.XMLNS,
 348                                                          dsPrefix);
 349         if (digestValue != null) {
 350             digestValueElem.appendChild
 351                 (ownerDoc.createTextNode(Base64.encode(digestValue)));
 352         }
 353         refElem.appendChild(digestValueElem);
 354 
 355         parent.appendChild(refElem);
 356         here = refElem.getAttributeNodeNS(null, "URI");
 357     }
 358 
 359     public void digest(XMLSignContext signContext)
 360         throws XMLSignatureException
 361     {
 362         Data data = null;
 363         if (appliedTransformData == null) {
 364             data = dereference(signContext);
 365         } else {
 366             data = appliedTransformData;
 367         }
 368         digestValue = transform(data, signContext);
 369 
 370         // insert digestValue into DigestValue element
 371         String encodedDV = Base64.encode(digestValue);
 372         if (log.isLoggable(java.util.logging.Level.FINE)) {
 373             log.log(java.util.logging.Level.FINE, "Reference object uri = " + uri);
 374         }
 375         Element digestElem = DOMUtils.getLastChildElement(refElem);
 376         if (digestElem == null) {
 377             throw new XMLSignatureException("DigestValue element expected");
 378         }
 379         DOMUtils.removeAllChildren(digestElem);
 380         digestElem.appendChild
 381             (refElem.getOwnerDocument().createTextNode(encodedDV));
 382 
 383         digested = true;
 384         if (log.isLoggable(java.util.logging.Level.FINE)) {
 385             log.log(java.util.logging.Level.FINE, "Reference digesting completed");
 386         }
 387     }
 388 
 389     public boolean validate(XMLValidateContext validateContext)
 390         throws XMLSignatureException
 391     {
 392         if (validateContext == null) {
 393             throw new NullPointerException("validateContext cannot be null");
 394         }
 395         if (validated) {
 396             return validationStatus;
 397         }
 398         Data data = dereference(validateContext);
 399         calcDigestValue = transform(data, validateContext);
 400 
 401         if (log.isLoggable(java.util.logging.Level.FINE)) {
 402             log.log(java.util.logging.Level.FINE, "Expected digest: " + Base64.encode(digestValue));
 403             log.log(java.util.logging.Level.FINE, "Actual digest: " + Base64.encode(calcDigestValue));
 404         }
 405 
 406         validationStatus = Arrays.equals(digestValue, calcDigestValue);
 407         validated = true;
 408         return validationStatus;
 409     }
 410 
 411     public Data getDereferencedData() {
 412         return derefData;
 413     }
 414 
 415     public InputStream getDigestInputStream() {
 416         return dis;
 417     }
 418 
 419     private Data dereference(XMLCryptoContext context)
 420         throws XMLSignatureException
 421     {
 422         Data data = null;
 423 
 424         // use user-specified URIDereferencer if specified; otherwise use deflt
 425         URIDereferencer deref = context.getURIDereferencer();
 426         if (deref == null) {
 427             deref = DOMURIDereferencer.INSTANCE;
 428         }
 429         try {
 430             data = deref.dereference(this, context);
 431             if (log.isLoggable(java.util.logging.Level.FINE)) {
 432                 log.log(java.util.logging.Level.FINE, "URIDereferencer class name: " + deref.getClass().getName());
 433                 log.log(java.util.logging.Level.FINE, "Data class name: " + data.getClass().getName());
 434             }
 435         } catch (URIReferenceException ure) {
 436             throw new XMLSignatureException(ure);
 437         }
 438 
 439         return data;
 440     }
 441 
 442     private byte[] transform(Data dereferencedData,
 443                              XMLCryptoContext context)
 444         throws XMLSignatureException
 445     {
 446         if (md == null) {
 447             try {
 448                 md = MessageDigest.getInstance
 449                     (((DOMDigestMethod)digestMethod).getMessageDigestAlgorithm());
 450             } catch (NoSuchAlgorithmException nsae) {
 451                 throw new XMLSignatureException(nsae);
 452             }
 453         }
 454         md.reset();
 455         DigesterOutputStream dos;
 456         Boolean cache = (Boolean)
 457             context.getProperty("javax.xml.crypto.dsig.cacheReference");
 458         if (cache != null && cache.booleanValue()) {
 459             this.derefData = copyDerefData(dereferencedData);
 460             dos = new DigesterOutputStream(md, true);
 461         } else {
 462             dos = new DigesterOutputStream(md);
 463         }
 464         OutputStream os = null;
 465         Data data = dereferencedData;
 466         try {
 467             os = new UnsyncBufferedOutputStream(dos);
 468             for (int i = 0, size = transforms.size(); i < size; i++) {
 469                 DOMTransform transform = (DOMTransform)transforms.get(i);
 470                 if (i < size - 1) {
 471                     data = transform.transform(data, context);
 472                 } else {
 473                     data = transform.transform(data, context, os);
 474                 }
 475             }
 476 
 477             if (data != null) {
 478                 XMLSignatureInput xi;
 479                 // explicitly use C14N 1.1 when generating signature
 480                 // first check system property, then context property
 481                 boolean c14n11 = useC14N11;
 482                 String c14nalg = CanonicalizationMethod.INCLUSIVE;
 483                 if (context instanceof XMLSignContext) {
 484                     if (!c14n11) {
 485                         Boolean prop = (Boolean)context.getProperty
 486                             ("com.sun.org.apache.xml.internal.security.useC14N11");
 487                         c14n11 = (prop != null && prop.booleanValue());
 488                         if (c14n11) {
 489                             c14nalg = "http://www.w3.org/2006/12/xml-c14n11";
 490                         }
 491                     } else {
 492                         c14nalg = "http://www.w3.org/2006/12/xml-c14n11";
 493                     }
 494                 }
 495                 if (data instanceof ApacheData) {
 496                     xi = ((ApacheData)data).getXMLSignatureInput();
 497                 } else if (data instanceof OctetStreamData) {
 498                     xi = new XMLSignatureInput
 499                         (((OctetStreamData)data).getOctetStream());
 500                 } else if (data instanceof NodeSetData) {
 501                     TransformService spi = null;
 502                     if (provider == null) {
 503                         spi = TransformService.getInstance(c14nalg, "DOM");
 504                     } else {
 505                         try {
 506                             spi = TransformService.getInstance(c14nalg, "DOM", provider);
 507                         } catch (NoSuchAlgorithmException nsae) {
 508                             spi = TransformService.getInstance(c14nalg, "DOM");
 509                         }
 510                     }
 511                     data = spi.transform(data, context);
 512                     xi = new XMLSignatureInput
 513                         (((OctetStreamData)data).getOctetStream());
 514                 } else {
 515                     throw new XMLSignatureException("unrecognized Data type");
 516                 }
 517                 if (context instanceof XMLSignContext && c14n11
 518                     && !xi.isOctetStream() && !xi.isOutputStreamSet()) {
 519                     TransformService spi = null;
 520                     if (provider == null) {
 521                         spi = TransformService.getInstance(c14nalg, "DOM");
 522                     } else {
 523                         try {
 524                             spi = TransformService.getInstance(c14nalg, "DOM", provider);
 525                         } catch (NoSuchAlgorithmException nsae) {
 526                             spi = TransformService.getInstance(c14nalg, "DOM");
 527                         }
 528                     }
 529 
 530                     DOMTransform t = new DOMTransform(spi);
 531                     Element transformsElem = null;
 532                     String dsPrefix = DOMUtils.getSignaturePrefix(context);
 533                     if (allTransforms.isEmpty()) {
 534                         transformsElem = DOMUtils.createElement(
 535                             refElem.getOwnerDocument(),
 536                             "Transforms", XMLSignature.XMLNS, dsPrefix);
 537                         refElem.insertBefore(transformsElem,
 538                             DOMUtils.getFirstChildElement(refElem));
 539                     } else {
 540                         transformsElem = DOMUtils.getFirstChildElement(refElem);
 541                     }
 542                     t.marshal(transformsElem, dsPrefix,
 543                               (DOMCryptoContext)context);
 544                     allTransforms.add(t);
 545                     xi.updateOutputStream(os, true);
 546                 } else {
 547                     xi.updateOutputStream(os);
 548                 }
 549             }
 550             os.flush();
 551             if (cache != null && cache.booleanValue()) {
 552                 this.dis = dos.getInputStream();
 553             }
 554             return dos.getDigestValue();
 555         } catch (NoSuchAlgorithmException e) {
 556             throw new XMLSignatureException(e);
 557         } catch (TransformException e) {
 558             throw new XMLSignatureException(e);
 559         } catch (MarshalException e) {
 560             throw new XMLSignatureException(e);
 561         } catch (IOException e) {
 562             throw new XMLSignatureException(e);
 563         } catch (com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException e) {
 564             throw new XMLSignatureException(e);
 565         } finally {
 566             if (os != null) {
 567                 try {
 568                     os.close();
 569                 } catch (IOException e) {
 570                     throw new XMLSignatureException(e);
 571                 }
 572             }
 573             if (dos != null) {
 574                 try {
 575                     dos.close();
 576                 } catch (IOException e) {
 577                     throw new XMLSignatureException(e);
 578                 }
 579             }
 580         }
 581     }
 582 
 583     public Node getHere() {
 584         return here;
 585     }
 586 
 587     @Override
 588     public boolean equals(Object o) {
 589         if (this == o) {
 590             return true;
 591         }
 592 
 593         if (!(o instanceof Reference)) {
 594             return false;
 595         }
 596         Reference oref = (Reference)o;
 597 
 598         boolean idsEqual = (id == null ? oref.getId() == null
 599                                        : id.equals(oref.getId()));
 600         boolean urisEqual = (uri == null ? oref.getURI() == null
 601                                          : uri.equals(oref.getURI()));
 602         boolean typesEqual = (type == null ? oref.getType() == null
 603                                            : type.equals(oref.getType()));
 604         boolean digestValuesEqual =
 605             Arrays.equals(digestValue, oref.getDigestValue());
 606 
 607         return digestMethod.equals(oref.getDigestMethod()) && idsEqual &&
 608             urisEqual && typesEqual &&
 609             allTransforms.equals(oref.getTransforms()) && digestValuesEqual;
 610     }
 611 
 612     @Override
 613     public int hashCode() {
 614         int result = 17;
 615         if (id != null) {
 616             result = 31 * result + id.hashCode();
 617         }
 618         if (uri != null) {
 619             result = 31 * result + uri.hashCode();
 620         }
 621         if (type != null) {
 622             result = 31 * result + type.hashCode();
 623         }
 624         if (digestValue != null) {
 625             result = 31 * result + Arrays.hashCode(digestValue);
 626         }
 627         result = 31 * result + digestMethod.hashCode();
 628         result = 31 * result + allTransforms.hashCode();
 629 
 630         return result;
 631     }
 632 
 633     boolean isDigested() {
 634         return digested;
 635     }
 636 
 637     private static Data copyDerefData(Data dereferencedData) {
 638         if (dereferencedData instanceof ApacheData) {
 639             // need to make a copy of the Data
 640             ApacheData ad = (ApacheData)dereferencedData;
 641             XMLSignatureInput xsi = ad.getXMLSignatureInput();
 642             if (xsi.isNodeSet()) {
 643                 try {
 644                     final Set<Node> s = xsi.getNodeSet();
 645                     return new NodeSetData() {
 646                         public Iterator<Node> iterator() { return s.iterator(); }
 647                     };
 648                 } catch (Exception e) {
 649                     // log a warning
 650                     log.log(java.util.logging.Level.WARNING, "cannot cache dereferenced data: " + e);
 651                     return null;
 652                 }
 653             } else if (xsi.isElement()) {
 654                 return new DOMSubTreeData
 655                     (xsi.getSubNode(), xsi.isExcludeComments());
 656             } else if (xsi.isOctetStream() || xsi.isByteArray()) {
 657                 try {
 658                     return new OctetStreamData
 659                         (xsi.getOctetStream(), xsi.getSourceURI(),
 660                          xsi.getMIMEType());
 661                 } catch (IOException ioe) {
 662                     // log a warning
 663                     log.log(java.util.logging.Level.WARNING, "cannot cache dereferenced data: " + ioe);
 664                     return null;
 665                 }
 666             }
 667         }
 668         return dereferencedData;
 669     }
 670 }