1 /*
   2  * Copyright (c) 2003, 2005, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.rmi.rmic.newrmic.jrmp;
  27 
  28 import com.sun.javadoc.ClassDoc;
  29 import java.io.File;
  30 import java.io.FileOutputStream;
  31 import java.io.IOException;
  32 import java.io.OutputStreamWriter;
  33 import java.util.Collections;
  34 import java.util.HashMap;
  35 import java.util.HashSet;
  36 import java.util.Map;
  37 import java.util.Set;
  38 import sun.rmi.rmic.newrmic.BatchEnvironment;
  39 import sun.rmi.rmic.newrmic.Generator;
  40 import sun.rmi.rmic.newrmic.IndentingWriter;
  41 import sun.rmi.rmic.newrmic.Main;
  42 import sun.rmi.rmic.newrmic.Resources;
  43 
  44 import static sun.rmi.rmic.newrmic.jrmp.Constants.*;
  45 
  46 /**
  47  * JRMP rmic back end; generates source code for JRMP stub and
  48  * skeleton classes.
  49  *
  50  * WARNING: The contents of this source file are not part of any
  51  * supported API.  Code that depends on them does so at its own risk:
  52  * they are subject to change or removal without notice.
  53  *
  54  * @author Peter Jones
  55  **/
  56 public class JrmpGenerator implements Generator {
  57 
  58     private static final Map<String,StubVersion> versionOptions =
  59         new HashMap<String,StubVersion>();
  60     static {
  61         versionOptions.put("-v1.1", StubVersion.V1_1);
  62         versionOptions.put("-vcompat", StubVersion.VCOMPAT);
  63         versionOptions.put("-v1.2", StubVersion.V1_2);
  64     }
  65 
  66     private static final Set<String> bootstrapClassNames =
  67         new HashSet<String>();
  68     static {
  69         bootstrapClassNames.add("java.lang.Exception");
  70         bootstrapClassNames.add("java.rmi.Remote");
  71         bootstrapClassNames.add("java.rmi.RemoteException");
  72         bootstrapClassNames.add("java.lang.RuntimeException");
  73     };
  74 
  75     /** version of the JRMP stub protocol to generate code for */
  76     private StubVersion version = StubVersion.V1_2;     // default is -v1.2
  77 
  78     /**
  79      * Creates a new JrmpGenerator.
  80      **/
  81     public JrmpGenerator() { }
  82 
  83     /**
  84      * The JRMP generator recognizes command line options for
  85      * selecting the JRMP stub protocol version to generate classes
  86      * for.  Only one such option is allowed.
  87      **/
  88     public boolean parseArgs(String[] args, Main main) {
  89         String explicitVersion = null;
  90         for (int i = 0; i < args.length; i++) {
  91             String arg = args[i];
  92             if (versionOptions.containsKey(arg)) {
  93                 if (explicitVersion != null && !explicitVersion.equals(arg)) {
  94                     main.error("rmic.cannot.use.both", explicitVersion, arg);
  95                     return false;
  96                 }
  97                 explicitVersion = arg;
  98                 version = versionOptions.get(arg);
  99                 args[i] = null;
 100             }
 101         }
 102         return true;
 103     }
 104 
 105     /**
 106      * The JRMP generator does not require an environment class more
 107      * specific than BatchEnvironment.
 108      **/
 109     public Class<? extends BatchEnvironment> envClass() {
 110         return BatchEnvironment.class;
 111     }
 112 
 113     public Set<String> bootstrapClassNames() {
 114         return Collections.unmodifiableSet(bootstrapClassNames);
 115     }
 116 
 117     /**
 118      * Generates the source file(s) for the JRMP stub class and
 119      * (optionally) skeleton class for the specified remote
 120      * implementation class.
 121      **/
 122     public void generate(BatchEnvironment env,
 123                          ClassDoc inputClass,
 124                          File destDir)
 125     {
 126         RemoteClass remoteClass = RemoteClass.forClass(env, inputClass);
 127         if (remoteClass == null) {
 128             return;     // an error must have occurred
 129         }
 130 
 131         StubSkeletonWriter writer =
 132             new StubSkeletonWriter(env, remoteClass, version);
 133 
 134         File stubFile = sourceFileForClass(writer.stubClassName(), destDir);
 135         try {
 136             IndentingWriter out = new IndentingWriter(
 137                 new OutputStreamWriter(new FileOutputStream(stubFile)));
 138             writer.writeStub(out);
 139             out.close();
 140             if (env.verbose()) {
 141                 env.output(Resources.getText("rmic.wrote",
 142                                              stubFile.getPath()));
 143             }
 144             env.addGeneratedFile(stubFile);
 145         } catch (IOException e) {
 146             env.error("rmic.cant.write", stubFile.toString());
 147             return;
 148         }
 149 
 150         File skeletonFile =
 151             sourceFileForClass(writer.skeletonClassName(), destDir);
 152         if (version == StubVersion.V1_1 ||
 153             version == StubVersion.VCOMPAT)
 154         {
 155             try {
 156                 IndentingWriter out = new IndentingWriter(
 157                     new OutputStreamWriter(
 158                         new FileOutputStream(skeletonFile)));
 159                 writer.writeSkeleton(out);
 160                 out.close();
 161                 if (env.verbose()) {
 162                     env.output(Resources.getText("rmic.wrote",
 163                                                  skeletonFile.getPath()));
 164                 }
 165                 env.addGeneratedFile(skeletonFile);
 166             } catch (IOException e) {
 167                 env.error("rmic.cant.write", skeletonFile.toString());
 168                 return;
 169             }
 170         } else {
 171             /*
 172              * If skeleton files are not being generated for this run,
 173              * delete old skeleton source or class files for this
 174              * remote implementation class that were (presumably) left
 175              * over from previous runs, to avoid user confusion from
 176              * extraneous or inconsistent generated files.
 177              */
 178             File skeletonClassFile =
 179                 classFileForClass(writer.skeletonClassName(), destDir);
 180 
 181             skeletonFile.delete();      // ignore failures (no big deal)
 182             skeletonClassFile.delete();
 183         }
 184     }
 185 
 186 
 187     /**
 188      * Returns the File object to be used as the source file for a
 189      * class with the specified binary name, with the specified
 190      * destination directory as the top of the package hierarchy.
 191      **/
 192     private File sourceFileForClass(String binaryName, File destDir) {
 193         return fileForClass(binaryName, destDir, ".java");
 194     }
 195 
 196     /**
 197      * Returns the File object to be used as the class file for a
 198      * class with the specified binary name, with the supplied
 199      * destination directory as the top of the package hierarchy.
 200      **/
 201     private File classFileForClass(String binaryName, File destDir) {
 202         return fileForClass(binaryName, destDir, ".class");
 203     }
 204 
 205     private File fileForClass(String binaryName, File destDir, String ext) {
 206         int i = binaryName.lastIndexOf('.');
 207         String classFileName = binaryName.substring(i + 1) + ext;
 208         if (i != -1) {
 209             String packageName = binaryName.substring(0, i);
 210             String packagePath = packageName.replace('.', File.separatorChar);
 211             File packageDir = new File(destDir, packagePath);
 212             /*
 213              * Make sure that the directory for this package exists.
 214              * We assume that the caller has verified that the top-
 215              * level destination directory exists, so we need not
 216              * worry about creating it unintentionally.
 217              */
 218             if (!packageDir.exists()) {
 219                 packageDir.mkdirs();
 220             }
 221             return new File(packageDir, classFileName);
 222         } else {
 223             return new File(destDir, classFileName);
 224         }
 225     }
 226 }