1 /*
   2  * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 package com.sun.classanalyzer;
  25 
  26 import java.io.BufferedReader;
  27 import java.io.File;
  28 import java.io.IOException;
  29 import java.io.InputStream;
  30 import java.io.InputStreamReader;
  31 import java.util.ArrayList;
  32 import java.util.Collections;
  33 import java.util.List;
  34 import java.util.Set;
  35 import java.util.TreeSet;
  36 
  37 /**
  38  *
  39  * @author Mandy Chung
  40  */
  41 public class ResourceFile implements Comparable<ResourceFile> {
  42     private final String pathname;
  43     protected final String name;
  44     private Module module;
  45 
  46     ResourceFile(String fname) {
  47         this.pathname = fname.replace('/', File.separatorChar);
  48         this.name = fname.replace(File.separatorChar, '/');
  49     }
  50 
  51     Module getModule() {
  52         return module;
  53     }
  54 
  55     void setModule(Module m) {
  56         if (module != null) {
  57             throw new RuntimeException("Module for " + this + " already set");
  58         }
  59         this.module = m;
  60     }
  61 
  62     String getName() {
  63         return name;
  64     }
  65 
  66     String getPathname() {
  67         return pathname;
  68     }
  69 
  70     @Override
  71     public String toString() {
  72         return name;
  73     }
  74 
  75     @Override
  76     public int compareTo(ResourceFile o) {
  77         return name.compareTo(o.name);
  78     }
  79 
  80     static Set<ResourceFile> resources = new TreeSet<ResourceFile>();
  81 
  82     static boolean isResource(String pathname) {
  83         String name = pathname.replace(File.separatorChar, '/');
  84         if (name.endsWith("META-INF/MANIFEST.MF")) {
  85             return false;
  86         }
  87         if (name.contains("META-INF/JCE_RSA.")) {
  88             return false;
  89         }
  90 
  91         return true;
  92     }
  93 
  94     static ResourceFile addResource(String fname, InputStream in) {
  95         ResourceFile res;
  96         fname = fname.replace(File.separatorChar, '/');
  97         if (fname.startsWith("META-INF/services")) {
  98             res = new ServiceProviderConfigFile(fname, in);
  99         } else {
 100             res = new ResourceFile(fname);
 101         }
 102         resources.add(res);
 103         return res;
 104     }
 105 
 106     static Set<ResourceFile> getAllResources() {
 107         return Collections.unmodifiableSet(resources);
 108     }
 109 
 110     static class ServiceProviderConfigFile extends ResourceFile {
 111         final List<String> providers = new ArrayList<String>();
 112         final String service;
 113         ServiceProviderConfigFile(String fname, InputStream in) {
 114             super(fname);
 115             readServiceConfiguration(in, providers);
 116             this.service = name.substring("META-INF/services".length() + 1, name.length());
 117         }
 118 
 119         @Override
 120         String getName() {
 121             if (providers.isEmpty()) {
 122                 return service;
 123             } else {
 124                 // just use the first one for matching
 125                 return providers.get(0);
 126             }
 127         }
 128 
 129         @Override
 130         public boolean equals(Object o) {
 131             if (o instanceof ServiceProviderConfigFile) {
 132                 ServiceProviderConfigFile sp = (ServiceProviderConfigFile) o;
 133                 if (service.equals(sp.service) && providers.size() == sp.providers.size()) {
 134                     List<String> tmp = new ArrayList<String>(providers);
 135                     if (tmp.removeAll(sp.providers)) {
 136                         return tmp.size() == 0;
 137                     }
 138                 }
 139             }
 140             return false;
 141         }
 142 
 143         public int hashCode() {
 144             int hash = 7;
 145             hash = 73 * hash + (this.providers != null ? this.providers.hashCode() : 0);
 146             hash = 73 * hash + (this.service != null ? this.service.hashCode() : 0);
 147             return hash;
 148         }
 149 
 150         @Override
 151         public int compareTo(ResourceFile o) {
 152             if (this.equals(o)) {
 153                 return 0;
 154             } else {
 155                 if (getName().compareTo(o.getName()) < 0) {
 156                     return -1;
 157                 } else {
 158                     return 1;
 159                 }
 160             }
 161         }
 162 
 163         @SuppressWarnings("empty-statement")
 164         void readServiceConfiguration(InputStream in, List<String> names) {
 165             BufferedReader br = null;
 166             try {
 167                 if (in != null) {
 168                     // Properties doesn't perserve the order of the input file
 169                     br = new BufferedReader(new InputStreamReader(in, "utf-8"));
 170                     int lc = 1;
 171                     while ((lc = parseLine(br, lc, names)) >= 0);
 172                 }
 173             } catch (IOException ex) {
 174                 throw new RuntimeException(ex);
 175             } finally {
 176                 if (br != null) {
 177                     try {
 178                         br.close();
 179                     } catch (IOException ex) {
 180                         throw new RuntimeException(ex);
 181                     }
 182                 }
 183             }
 184         }
 185 
 186         // Parse a single line from the given configuration file, adding the name
 187         // on the line to the names list.
 188         //
 189         private int parseLine(BufferedReader r, int lc, List<String> names) throws IOException {
 190             String ln = r.readLine();
 191             if (ln == null) {
 192                 return -1;
 193             }
 194             int ci = ln.indexOf('#');
 195             if (ci >= 0) {
 196                 ln = ln.substring(0, ci);
 197             }
 198             ln = ln.trim();
 199             int n = ln.length();
 200             if (n != 0) {
 201                 if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0)) {
 202                     throw new RuntimeException("Illegal configuration-file syntax");
 203                 }
 204                 int cp = ln.codePointAt(0);
 205                 if (!Character.isJavaIdentifierStart(cp)) {
 206                     throw new RuntimeException("Illegal provider-class name: " + ln);
 207                 }
 208                 for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
 209                     cp = ln.codePointAt(i);
 210                     if (!Character.isJavaIdentifierPart(cp) && (cp != '.')) {
 211                         throw new RuntimeException("Illegal provider-class name: " + ln);
 212                     }
 213                 }
 214                 if (!names.contains(ln)) {
 215                     names.add(ln);
 216                 }
 217             }
 218             return lc + 1;
 219         }
 220     }
 221 }