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
26package sun.rmi.rmic.newrmic.jrmp;
27
28import com.sun.javadoc.ClassDoc;
29import java.io.File;
30import java.io.FileOutputStream;
31import java.io.IOException;
32import java.io.OutputStreamWriter;
33import java.util.Collections;
34import java.util.HashMap;
35import java.util.HashSet;
36import java.util.Map;
37import java.util.Set;
38import sun.rmi.rmic.newrmic.BatchEnvironment;
39import sun.rmi.rmic.newrmic.Generator;
40import sun.rmi.rmic.newrmic.IndentingWriter;
41import sun.rmi.rmic.newrmic.Main;
42import sun.rmi.rmic.newrmic.Resources;
43
44import 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 **/
56public 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}
227