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  */
  40 public class ResourceFile implements Comparable<ResourceFile> {
  41     private final String pathname;
  42     private final long filesize;
  43     protected final String name;
  44     private Module module;
  45 
  46     ResourceFile(String fname) {
  47         this(fname, 0);
  48     }
  49 
  50     ResourceFile(String fname, long size) {
  51         this.pathname = fname.replace('/', File.separatorChar);
  52         this.name = fname.replace(File.separatorChar, '/');
  53         this.filesize = size;
  54     }
  55 
  56     Module getModule() {
  57         return module;
  58     }
  59 
  60     void setModule(Module m) {
  61         if (module != null) {
  62             throw new RuntimeException("Module for " + this + " already set");
  63         }
  64         this.module = m;
  65     }
  66 
  67     String getName() {
  68         return name;
  69     }
  70 
  71     String getPathname() {
  72         return pathname;
  73     }
  74 
  75     long getFileSize() {
  76         return filesize;
  77     }
  78 
  79     boolean isService() {
  80         return pathname.startsWith("META-INF/services") ? true : false;
  81     }
  82 
  83     @Override
  84     public String toString() {
  85         return name;
  86     }
  87 
  88     @Override
  89     public int compareTo(ResourceFile o) {
  90         return name.compareTo(o.name);
  91     }
  92 
  93     private static Set<ResourceFile> resources = new TreeSet<ResourceFile>();
  94 
  95     static boolean isResource(String pathname) {
  96         // skip these files
  97         String name = pathname.replace(File.separatorChar, '/');
  98         if (name.endsWith("META-INF/MANIFEST.MF")) {
  99             return false;
 100         }
 101         if (name.contains("META-INF/JCE_RSA.")) {
 102             return false;
 103         }
 104         if (name.contains("META-INF/") &&
 105                 (name.endsWith(".RSA") || name.endsWith(".SF"))) {
 106             return false;
 107         }
 108 
 109         return true;
 110     }
 111 
 112     static ResourceFile getResource(String fname, InputStream in, long size) {
 113         ResourceFile res;
 114         fname = fname.replace(File.separatorChar, '/');
 115         if (fname.startsWith("META-INF/services")) {
 116             res = new ServiceProviderConfigFile(fname, in, size);
 117         } else {
 118             res = new ResourceFile(fname, size);
 119         }
 120         return res;
 121     }
 122 
 123     static ResourceFile addResource(String fname, InputStream in, long size) {
 124         ResourceFile res = getResource(fname, in, size);
 125         resources.add(res);
 126         return res;
 127     }
 128 
 129 
 130     static Set<ResourceFile> getAllResources() {
 131         return Collections.unmodifiableSet(resources);
 132     }
 133 
 134     static class ServiceProviderConfigFile extends ResourceFile {
 135         final List<String> providers = new ArrayList<String>();
 136         final String service;
 137 
 138         ServiceProviderConfigFile(String fname, InputStream in) {
 139             this(fname, in, 0);
 140         }
 141 
 142         ServiceProviderConfigFile(String fname, InputStream in, long size) {
 143             super(fname, size);
 144             readServiceConfiguration(in, providers);
 145             this.service = name.substring("META-INF/services".length() + 1, name.length());
 146         }
 147 
 148         @Override
 149         boolean isService() {
 150             return true;
 151         }
 152 
 153         @Override
 154         String getName() {
 155             if (providers.isEmpty()) {
 156                 return service;
 157             } else {
 158                 // just use the first one for matching
 159                 return providers.get(0);
 160             }
 161         }
 162 
 163         @Override
 164         public boolean equals(Object o) {
 165             if (o instanceof ServiceProviderConfigFile) {
 166                 ServiceProviderConfigFile sp = (ServiceProviderConfigFile) o;
 167                 if (service.equals(sp.service) && providers.size() == sp.providers.size()) {
 168                     List<String> tmp = new ArrayList<String>(providers);
 169                     if (tmp.removeAll(sp.providers)) {
 170                         return tmp.size() == 0;
 171                     }
 172                 }
 173             }
 174             return false;
 175         }
 176 
 177         public int hashCode() {
 178             int hash = 7;
 179             hash = 73 * hash + (this.providers != null ? this.providers.hashCode() : 0);
 180             hash = 73 * hash + (this.service != null ? this.service.hashCode() : 0);
 181             return hash;
 182         }
 183 
 184         @Override
 185         public int compareTo(ResourceFile o) {
 186             if (this.equals(o)) {
 187                 return 0;
 188             } else {
 189                 if (getName().compareTo(o.getName()) < 0) {
 190                     return -1;
 191                 } else {
 192                     return 1;
 193                 }
 194             }
 195         }
 196 
 197         @SuppressWarnings("empty-statement")
 198         void readServiceConfiguration(InputStream in, List<String> names) {
 199             BufferedReader br = null;
 200             try {
 201                 if (in != null) {
 202                     // Properties doesn't perserve the order of the input file
 203                     br = new BufferedReader(new InputStreamReader(in, "utf-8"));
 204                     int lc = 1;
 205                     while ((lc = parseLine(br, lc, names)) >= 0);
 206                 }
 207             } catch (IOException ex) {
 208                 throw new RuntimeException(ex);
 209             } finally {
 210                 if (br != null) {
 211                     try {
 212                         br.close();
 213                     } catch (IOException ex) {
 214                         throw new RuntimeException(ex);
 215                     }
 216                 }
 217             }
 218         }
 219 
 220         // Parse a single line from the given configuration file, adding the name
 221         // on the line to the names list.
 222         //
 223         private int parseLine(BufferedReader r, int lc, List<String> names) throws IOException {
 224             String ln = r.readLine();
 225             if (ln == null) {
 226                 return -1;
 227             }
 228             int ci = ln.indexOf('#');
 229             if (ci >= 0) {
 230                 ln = ln.substring(0, ci);
 231             }
 232             ln = ln.trim();
 233             int n = ln.length();
 234             if (n != 0) {
 235                 if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0)) {
 236                     throw new RuntimeException("Illegal configuration-file syntax");
 237                 }
 238                 int cp = ln.codePointAt(0);
 239                 if (!Character.isJavaIdentifierStart(cp)) {
 240                     throw new RuntimeException("Illegal provider-class name: " + ln);
 241                 }
 242                 for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
 243                     cp = ln.codePointAt(i);
 244                     if (!Character.isJavaIdentifierPart(cp) && (cp != '.')) {
 245                         throw new RuntimeException("Illegal provider-class name: " + ln);
 246                     }
 247                 }
 248                 if (!names.contains(ln)) {
 249                     names.add(ln);
 250                 }
 251             }
 252             return lc + 1;
 253         }
 254     }
 255 }