|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  */ | 
|  |  | 
|  | package sun.security.x509; | 
|  |  | 
|  | import java.io.*; | 
|  | import java.util.*; | 
|  |  | 
|  | import sun.security.util.*; | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  */ | 
|  | public class GeneralSubtrees implements Cloneable { | 
|  |  | 
|  |     private final List<GeneralSubtree> trees; | 
|  |  | 
|  |      | 
|  |     private static final int NAME_DIFF_TYPE = GeneralNameInterface.NAME_DIFF_TYPE; | 
|  |     private static final int NAME_MATCH = GeneralNameInterface.NAME_MATCH; | 
|  |     private static final int NAME_NARROWS = GeneralNameInterface.NAME_NARROWS; | 
|  |     private static final int NAME_WIDENS = GeneralNameInterface.NAME_WIDENS; | 
|  |     private static final int NAME_SAME_TYPE = GeneralNameInterface.NAME_SAME_TYPE; | 
|  |  | 
|  |      | 
|  |  | 
|  |      */ | 
|  |     public GeneralSubtrees() { | 
|  |         trees = new ArrayList<>(); | 
|  |     } | 
|  |  | 
|  |     private GeneralSubtrees(GeneralSubtrees source) { | 
|  |         trees = new ArrayList<>(source.trees); | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |  | 
|  |  | 
|  |      */ | 
|  |     public GeneralSubtrees(DerValue val) throws IOException { | 
|  |         this(); | 
|  |         if (val.tag != DerValue.tag_Sequence) { | 
|  |             throw new IOException("Invalid encoding of GeneralSubtrees."); | 
|  |         } | 
|  |         while (val.data.available() != 0) { | 
|  |             DerValue opt = val.data.getDerValue(); | 
|  |             GeneralSubtree tree = new GeneralSubtree(opt); | 
|  |             add(tree); | 
|  |         } | 
|  |     } | 
|  |  | 
|  |     public GeneralSubtree get(int index) { | 
|  |         return trees.get(index); | 
|  |     } | 
|  |  | 
|  |     public void remove(int index) { | 
|  |         trees.remove(index); | 
|  |     } | 
|  |  | 
|  |     public void add(GeneralSubtree tree) { | 
|  |         if (tree == null) { | 
|  |             throw new NullPointerException(); | 
|  |         } | 
|  |         trees.add(tree); | 
|  |     } | 
|  |  | 
|  |     public boolean contains(GeneralSubtree tree) { | 
|  |         if (tree == null) { | 
|  |             throw new NullPointerException(); | 
|  |         } | 
|  |         return trees.contains(tree); | 
|  |     } | 
|  |  | 
|  |     public int size() { | 
|  |         return trees.size(); | 
|  |     } | 
|  |  | 
|  |     public Iterator<GeneralSubtree> iterator() { | 
|  |         return trees.iterator(); | 
|  |     } | 
|  |  | 
|  |     public List<GeneralSubtree> trees() { | 
|  |         return trees; | 
|  |     } | 
|  |  | 
|  |     public Object clone() { | 
|  |         return new GeneralSubtrees(this); | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |      */ | 
|  |     public String toString() { | 
|  |         return "   GeneralSubtrees:\n" + trees + '\n'; | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |  | 
|  |  | 
|  |      */ | 
|  |     public void encode(DerOutputStream out) throws IOException { | 
|  |         DerOutputStream seq = new DerOutputStream(); | 
|  |  | 
|  |         for (int i = 0, n = size(); i < n; i++) { | 
|  |             get(i).encode(seq); | 
|  |         } | 
|  |         out.write(DerValue.tag_Sequence, seq); | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |      */ | 
|  |     public boolean equals(Object obj) { | 
|  |         if (this == obj) { | 
|  |             return true; | 
|  |         } | 
|  |         if (obj instanceof GeneralSubtrees == false) { | 
|  |             return false; | 
|  |         } | 
|  |         GeneralSubtrees other = (GeneralSubtrees)obj; | 
|  |         return this.trees.equals(other.trees); | 
|  |     } | 
|  |  | 
|  |     public int hashCode() { | 
|  |         return trees.hashCode(); | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |      */ | 
|  |     private GeneralNameInterface getGeneralNameInterface(int ndx) { | 
|  |         return getGeneralNameInterface(get(ndx)); | 
|  |     } | 
|  |  | 
|  |     private static GeneralNameInterface getGeneralNameInterface(GeneralSubtree gs) { | 
|  |         GeneralName gn = gs.getName(); | 
|  |         GeneralNameInterface gni = gn.getName(); | 
|  |         return gni; | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |  | 
|  |      */ | 
|  |     private void minimize() { | 
|  |  | 
|  |         // Algorithm: compare each entry n to all subsequent entries in | 
|  |         // the list: if any subsequent entry matches or widens entry n, | 
|  |         // remove entry n. If any subsequent entries narrow entry n, remove | 
|  |          | 
|  |         for (int i = 0; i < (size() - 1); i++) { | 
|  |             GeneralNameInterface current = getGeneralNameInterface(i); | 
|  |             boolean remove1 = false; | 
|  |  | 
|  |              | 
|  |             for (int j = i + 1; j < size(); j++) { | 
|  |                 GeneralNameInterface subsequent = getGeneralNameInterface(j); | 
|  |                 switch (current.constrains(subsequent)) { | 
|  |                     case GeneralNameInterface.NAME_DIFF_TYPE: | 
|  |                          | 
|  |                         continue; | 
|  |                     case GeneralNameInterface.NAME_MATCH: | 
|  |                          | 
|  |                         remove1 = true; | 
|  |                         break; | 
|  |                     case GeneralNameInterface.NAME_NARROWS: | 
|  |                         /* subsequent narrows current */ | 
|  |                          | 
|  |                         remove(j); | 
|  |                         j--;  | 
|  |                         continue; | 
|  |                     case GeneralNameInterface.NAME_WIDENS: | 
|  |                         /* subsequent widens current */ | 
|  |                          | 
|  |                         remove1 = true; | 
|  |                         break; | 
|  |                     case GeneralNameInterface.NAME_SAME_TYPE: | 
|  |                          | 
|  |                         continue; | 
|  |                 } | 
|  |                 break; | 
|  |             } /* end of this pass of subsequent elements */ | 
|  |  | 
|  |             if (remove1) { | 
|  |                 remove(i); | 
|  |                 i--; /* check the new i value */ | 
|  |             } | 
|  |  | 
|  |         } | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |      */ | 
|  |     private GeneralSubtree createWidestSubtree(GeneralNameInterface name) { | 
|  |         try { | 
|  |             GeneralName newName; | 
|  |             switch (name.getType()) { | 
|  |             case GeneralNameInterface.NAME_ANY: | 
|  |                 // Create new OtherName with same OID as baseName, but | 
|  |                  | 
|  |                 ObjectIdentifier otherOID = ((OtherName)name).getOID(); | 
|  |                 newName = new GeneralName(new OtherName(otherOID, null)); | 
|  |                 break; | 
|  |             case GeneralNameInterface.NAME_RFC822: | 
|  |                 newName = new GeneralName(new RFC822Name("")); | 
|  |                 break; | 
|  |             case GeneralNameInterface.NAME_DNS: | 
|  |                 newName = new GeneralName(new DNSName("")); | 
|  |                 break; | 
|  |             case GeneralNameInterface.NAME_X400: | 
|  |                 newName = new GeneralName(new X400Address((byte[])null)); | 
|  |                 break; | 
|  |             case GeneralNameInterface.NAME_DIRECTORY: | 
|  |                 newName = new GeneralName(new X500Name("")); | 
|  |                 break; | 
|  |             case GeneralNameInterface.NAME_EDI: | 
|  |                 newName = new GeneralName(new EDIPartyName("")); | 
|  |                 break; | 
|  |             case GeneralNameInterface.NAME_URI: | 
|  |                 newName = new GeneralName(new URIName("")); | 
|  |                 break; | 
|  |             case GeneralNameInterface.NAME_IP: | 
|  |                 newName = new GeneralName(new IPAddressName((byte[])null)); | 
|  |                 break; | 
|  |             case GeneralNameInterface.NAME_OID: | 
|  |                 newName = new GeneralName | 
|  |                     (new OIDName(new ObjectIdentifier((int[])null))); | 
|  |                 break; | 
|  |             default: | 
|  |                 throw new IOException | 
|  |                     ("Unsupported GeneralNameInterface type: " + name.getType()); | 
|  |             } | 
|  |             return new GeneralSubtree(newName, 0, -1); | 
|  |         } catch (IOException e) { | 
|  |             throw new RuntimeException("Unexpected error: " + e, e); | 
|  |         } | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |      */ | 
|  |     public GeneralSubtrees intersect(GeneralSubtrees other) { | 
|  |  | 
|  |         if (other == null) { | 
|  |             throw new NullPointerException("other GeneralSubtrees must not be null"); | 
|  |         } | 
|  |  | 
|  |         GeneralSubtrees newThis = new GeneralSubtrees(); | 
|  |         GeneralSubtrees newExcluded = null; | 
|  |  | 
|  |         // Step 1: If this is empty, just add everything in other to this and | 
|  |          | 
|  |         if (size() == 0) { | 
|  |             union(other); | 
|  |             return null; | 
|  |         } | 
|  |  | 
|  |         // Step 2: For ease of checking the subtrees, minimize them by | 
|  |         // constructing versions that contain only the widest instance of | 
|  |          | 
|  |         this.minimize(); | 
|  |         other.minimize(); | 
|  |  | 
|  |         // Step 3: Check each entry in this to see whether we keep it or | 
|  |         // remove it, and whether we add anything to newExcluded or newThis. | 
|  |         // We keep an entry in this unless it is narrowed by all entries in | 
|  |         // other.  We add an entry to newExcluded if there is at least one | 
|  |         // entry of the same nameType in other, but this entry does | 
|  |         // not share the same subtree with any of the entries in other. | 
|  |         // We add an entry from other to newThis if there is no name of the | 
|  |          | 
|  |         for (int i = 0; i < size(); i++) { | 
|  |             GeneralNameInterface thisEntry = getGeneralNameInterface(i); | 
|  |             boolean removeThisEntry = false; | 
|  |  | 
|  |             // Step 3a: If the widest name of this type in other narrows | 
|  |             // thisEntry, remove thisEntry and add widest other to newThis. | 
|  |             // Simultaneously, check for situation where there is a name of | 
|  |             // this type in other, but no name in other matches, narrows, | 
|  |              | 
|  |             boolean sameType = false; | 
|  |             for (int j = 0; j < other.size(); j++) { | 
|  |                 GeneralSubtree otherEntryGS = other.get(j); | 
|  |                 GeneralNameInterface otherEntry = | 
|  |                     getGeneralNameInterface(otherEntryGS); | 
|  |                 switch (thisEntry.constrains(otherEntry)) { | 
|  |                     case NAME_NARROWS: | 
|  |                         remove(i); | 
|  |                         i--; | 
|  |                         newThis.add(otherEntryGS); | 
|  |                         sameType = false; | 
|  |                         break; | 
|  |                     case NAME_SAME_TYPE: | 
|  |                         sameType = true; | 
|  |                         continue; | 
|  |                     case NAME_MATCH: | 
|  |                     case NAME_WIDENS: | 
|  |                         sameType = false; | 
|  |                         break; | 
|  |                     case NAME_DIFF_TYPE: | 
|  |                     default: | 
|  |                         continue; | 
|  |                 } | 
|  |                 break; | 
|  |             } | 
|  |  | 
|  |             // Step 3b: If sameType is still true, we have the situation | 
|  |             // where there was a name of the same type as thisEntry in | 
|  |             // other, but no name in other widened, matched, or narrowed | 
|  |              | 
|  |             if (sameType) { | 
|  |  | 
|  |                 // Step 3b.1: See if there are any entries in this and other | 
|  |                 // with this type that match, widen, or narrow each other. | 
|  |                 // If not, then we need to add a "widest subtree" of this | 
|  |                  | 
|  |                 boolean intersection = false; | 
|  |                 for (int j = 0; j < size(); j++) { | 
|  |                     GeneralNameInterface thisAltEntry = getGeneralNameInterface(j); | 
|  |  | 
|  |                     if (thisAltEntry.getType() == thisEntry.getType()) { | 
|  |                         for (int k = 0; k < other.size(); k++) { | 
|  |                             GeneralNameInterface othAltEntry = | 
|  |                                 other.getGeneralNameInterface(k); | 
|  |  | 
|  |                             int constraintType = | 
|  |                                 thisAltEntry.constrains(othAltEntry); | 
|  |                             if (constraintType == NAME_MATCH || | 
|  |                                 constraintType == NAME_WIDENS || | 
|  |                                 constraintType == NAME_NARROWS) { | 
|  |                                 intersection = true; | 
|  |                                 break; | 
|  |                             } | 
|  |                         } | 
|  |                     } | 
|  |                 } | 
|  |                 if (intersection == false) { | 
|  |                     if (newExcluded == null) { | 
|  |                         newExcluded = new GeneralSubtrees(); | 
|  |                     } | 
|  |                     GeneralSubtree widestSubtree = | 
|  |                          createWidestSubtree(thisEntry); | 
|  |                     if (!newExcluded.contains(widestSubtree)) { | 
|  |                         newExcluded.add(widestSubtree); | 
|  |                     } | 
|  |                 } | 
|  |  | 
|  |                  | 
|  |                 remove(i); | 
|  |                 i--; | 
|  |             } | 
|  |         } | 
|  |  | 
|  |          | 
|  |         if (newThis.size() > 0) { | 
|  |             union(newThis); | 
|  |         } | 
|  |  | 
|  |         // Step 5: Add all entries in other that do not have any entry of the | 
|  |          | 
|  |         for (int i = 0; i < other.size(); i++) { | 
|  |             GeneralSubtree otherEntryGS = other.get(i); | 
|  |             GeneralNameInterface otherEntry = getGeneralNameInterface(otherEntryGS); | 
|  |             boolean diffType = false; | 
|  |             for (int j = 0; j < size(); j++) { | 
|  |                 GeneralNameInterface thisEntry = getGeneralNameInterface(j); | 
|  |                 switch (thisEntry.constrains(otherEntry)) { | 
|  |                     case NAME_DIFF_TYPE: | 
|  |                         diffType = true; | 
|  |                         // continue to see if we find something later of the | 
|  |                          | 
|  |                         continue; | 
|  |                     case NAME_NARROWS: | 
|  |                     case NAME_SAME_TYPE: | 
|  |                     case NAME_MATCH: | 
|  |                     case NAME_WIDENS: | 
|  |                         diffType = false;  | 
|  |                         // break because we know we won't be adding it to | 
|  |                          | 
|  |                         break; | 
|  |                     default: | 
|  |                         continue; | 
|  |                 } | 
|  |                 break; | 
|  |             } | 
|  |             if (diffType) { | 
|  |                 add(otherEntryGS); | 
|  |             } | 
|  |         } | 
|  |  | 
|  |          | 
|  |         return newExcluded; | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |  | 
|  |  | 
|  |      */ | 
|  |     public void union(GeneralSubtrees other) { | 
|  |         if (other != null) { | 
|  |             for (int i = 0, n = other.size(); i < n; i++) { | 
|  |                 add(other.get(i)); | 
|  |             } | 
|  |              | 
|  |             minimize(); | 
|  |         } | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |      */ | 
|  |     public void reduce(GeneralSubtrees excluded) { | 
|  |         if (excluded == null) { | 
|  |             return; | 
|  |         } | 
|  |         for (int i = 0, n = excluded.size(); i < n; i++) { | 
|  |             GeneralNameInterface excludedName = excluded.getGeneralNameInterface(i); | 
|  |             for (int j = 0; j < size(); j++) { | 
|  |                 GeneralNameInterface permitted = getGeneralNameInterface(j); | 
|  |                 switch (excludedName.constrains(permitted)) { | 
|  |                 case GeneralNameInterface.NAME_DIFF_TYPE: | 
|  |                     break; | 
|  |                 case GeneralNameInterface.NAME_MATCH: | 
|  |                     remove(j); | 
|  |                     j--; | 
|  |                     break; | 
|  |                 case GeneralNameInterface.NAME_NARROWS: | 
|  |                      | 
|  |                     remove(j); | 
|  |                     j--; | 
|  |                     break; | 
|  |                 case GeneralNameInterface.NAME_WIDENS: | 
|  |                      | 
|  |                     break; | 
|  |                 case GeneralNameInterface.NAME_SAME_TYPE: | 
|  |                     break; | 
|  |                 } | 
|  |             } /* end of this pass of permitted */ | 
|  |         } /* end of pass of excluded */ | 
|  |     } | 
|  | } |