1 /*
   2  * Licensed to the Apache Software Foundation (ASF) under one or more
   3  * contributor license agreements.  See the NOTICE file distributed with
   4  * this work for additional information regarding copyright ownership.
   5  * The ASF licenses this file to You under the Apache License, Version 2.0
   6  * (the "License"); you may not use this file except in compliance with
   7  * the License.  You may obtain a copy of the License at
   8  *
   9  *      http://www.apache.org/licenses/LICENSE-2.0
  10  *
  11  * Unless required by applicable law or agreed to in writing, software
  12  * distributed under the License is distributed on an "AS IS" BASIS,
  13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14  * See the License for the specific language governing permissions and
  15  * limitations under the License.
  16  */
  17 
  18 package com.sun.org.apache.xml.internal.resolver.helpers;
  19 
  20 /**
  21  * Static methods for dealing with public identifiers.
  22  *
  23  * <p>This class defines a set of static methods that can be called
  24  * to handle public identifiers.</p>
  25  *
  26  * @author Norman Walsh
  27  * <a href="mailto:Norman.Walsh@Sun.COM">Norman.Walsh@Sun.COM</a>
  28  *
  29  */
  30 public abstract class PublicId {
  31 
  32   protected PublicId() {}
  33 
  34   /**
  35    * Normalize a public identifier.
  36    *
  37    * <p>Public identifiers must be normalized according to the following
  38    * rules before comparisons between them can be made:</p>
  39    *
  40    * <ul>
  41    * <li>Whitespace characters are normalized to spaces (e.g., line feeds,
  42    * tabs, etc. become spaces).</li>
  43    * <li>Leading and trailing whitespace is removed.</li>
  44    * <li>Multiple internal whitespaces are normalized to a single
  45    * space.</li>
  46    * </ul>
  47    *
  48    * <p>This method is declared static so that other classes
  49    * can use it directly.</p>
  50    *
  51    * @param publicId The unnormalized public identifier.
  52    *
  53    * @return The normalized identifier.
  54    */
  55   public static String normalize(String publicId) {
  56       String normal = publicId.replace('\t', ' ');
  57       normal = normal.replace('\r', ' ');
  58       normal = normal.replace('\n', ' ');
  59       normal = normal.trim();
  60 
  61       int pos;
  62 
  63       while ((pos = normal.indexOf("  ")) >= 0) {
  64           normal = normal.substring(0, pos) + normal.substring(pos+1);
  65       }
  66       return normal;
  67   }
  68 
  69   /**
  70    * Encode a public identifier as a "publicid" URN.
  71    *
  72    * <p>This method is declared static so that other classes
  73    * can use it directly.</p>
  74    *
  75    * @param publicId The unnormalized public identifier.
  76    *
  77    * @return The normalized identifier.
  78    */
  79   public static String encodeURN(String publicId) {
  80       String urn = PublicId.normalize(publicId);
  81 
  82       urn = PublicId.stringReplace(urn, "%", "%25");
  83       urn = PublicId.stringReplace(urn, ";", "%3B");
  84       urn = PublicId.stringReplace(urn, "'", "%27");
  85       urn = PublicId.stringReplace(urn, "?", "%3F");
  86       urn = PublicId.stringReplace(urn, "#", "%23");
  87       urn = PublicId.stringReplace(urn, "+", "%2B");
  88       urn = PublicId.stringReplace(urn, " ", "+");
  89       urn = PublicId.stringReplace(urn, "::", ";");
  90       urn = PublicId.stringReplace(urn, ":", "%3A");
  91       urn = PublicId.stringReplace(urn, "//", ":");
  92       urn = PublicId.stringReplace(urn, "/", "%2F");
  93 
  94       StringBuilder buffer = new StringBuilder(13 + urn.length());
  95       buffer.append("urn:publicid:");
  96       buffer.append(urn);
  97       return buffer.toString();
  98   }
  99 
 100   /**
 101    * Decode a "publicid" URN into a public identifier.
 102    *
 103    * <p>This method is declared static so that other classes
 104    * can use it directly.</p>
 105    *
 106    * @param urn The urn:publicid: URN
 107    *
 108    * @return The normalized identifier.
 109    */
 110   public static String decodeURN(String urn) {
 111       String publicId;
 112       if (urn.startsWith("urn:publicid:")) {
 113           publicId = urn.substring(13);
 114       }
 115       else {
 116           return urn;
 117       }
 118 
 119       final boolean hasEscape = (publicId.indexOf('%') >= 0);
 120       if (hasEscape) {
 121           publicId = PublicId.stringReplace(publicId, "%2F", "/");
 122       }
 123       publicId = PublicId.stringReplace(publicId, ":", "//");
 124       if (hasEscape) {
 125           publicId = PublicId.stringReplace(publicId, "%3A", ":");
 126       }
 127       publicId = PublicId.stringReplace(publicId, ";", "::");
 128       publicId = PublicId.stringReplace(publicId, "+", " ");
 129       if (hasEscape) {
 130           publicId = PublicId.stringReplace(publicId, "%2B", "+");
 131           publicId = PublicId.stringReplace(publicId, "%23", "#");
 132           publicId = PublicId.stringReplace(publicId, "%3F", "?");
 133           publicId = PublicId.stringReplace(publicId, "%27", "'");
 134           publicId = PublicId.stringReplace(publicId, "%3B", ";");
 135           publicId = PublicId.stringReplace(publicId, "%25", "%");
 136       }
 137 
 138       return publicId;
 139   }
 140 
 141   /**
 142    * Replace one string with another.
 143    */
 144   private static String stringReplace(String str,
 145           String oldStr,
 146           String newStr) {
 147       int pos = str.indexOf(oldStr);
 148       if (pos >= 0) {
 149           final StringBuilder buffer = new StringBuilder();
 150           final int oldStrLength = oldStr.length();
 151           int start = 0;
 152           do {
 153               for (int i = start; i < pos; ++i) {
 154                   buffer.append(str.charAt(i));
 155               }
 156               buffer.append(newStr);
 157               start = pos + oldStrLength;
 158               pos = str.indexOf(oldStr, start);
 159           }
 160           while (pos >= 0);
 161           final int strLength = str.length();
 162           for (int i = start; i < strLength; ++i) {
 163               buffer.append(str.charAt(i));
 164           }
 165           return buffer.toString();
 166       }
 167       return str;
 168   }
 169 }