blob: 6d895a14cd35114351316732fee8977c6433ceb9 [file] [log] [blame]
/* GiopRmicCompiler -- Central GIOP-based RMI stub and tie compiler class.
Copyright (C) 2006 Free Software Foundation
This file is part of GNU Classpath.
GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.
*/
package gnu.classpath.tools.giop.grmic;
import gnu.classpath.tools.AbstractMethodGenerator;
import java.io.File;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.TreeSet;
/**
* Provides the extended rmic functionality to generate the POA - based classes
* for GIOP (javax.rmi.CORBA package).
*
* @author Audrius Meskauskas, Lithuania (audriusa@Bioinformatics.org)
*/
public class GiopRmicCompiler
extends Generator implements Comparator
{
/** The package name. */
protected String packag;
/**
* The "basic" name (normally, the interface name, unless several Remote -
* derived interfaces are implemented.
*/
protected String name;
/**
* The name (without package) of the class, passed as the parameter.
*/
protected String implName;
/**
* The proposed name for the stub.
*/
protected String stubName;
/**
* The Remote's, implemented by this class.
*/
protected Collection implementedRemotes = new HashSet();
/**
* The extra classes that must be imported.
*/
protected Collection extraImports = new HashSet();
/**
* The methods we must implement.
*/
protected Collection methods = new HashSet();
/**
* The map of all code generator variables.
*/
public Properties vars = new Properties();
/**
* If this flag is set (true by default), the compiler generates the Servant
* based classes. If set to false, the compiler generates the old style
* ObjectImpl based classes.
*/
protected boolean poaMode = true;
/**
* If this flag is set (true by default), the compiler emits warnings.
*/
protected boolean warnings = true;
/**
* Verbose output
*/
protected boolean verbose = false;
/**
* Force mode - do not check the exceptions
*/
protected boolean force = false;
/**
* The class loader to load the class being compiled.
*/
ClassLoader classLoader;
/**
* Clear data, preparing for the next compilation.
*/
public void reset()
{
packag = name = implName = stubName = null;
implementedRemotes.clear();
extraImports.clear();
methods.clear();
vars.clear();
}
/**
* Set the class path (handle the -classpath key)
*
* @param classPath the class path to set.
*/
public void setClassPath(String classPath)
{
classLoader = Thread.currentThread().getContextClassLoader();
StringTokenizer tok = new StringTokenizer(classPath, File.pathSeparator,
true);
ArrayList urls = new ArrayList(tok.countTokens());
String s = null;
try
{
while (tok.hasMoreTokens())
{
s = tok.nextToken();
if (s.equals(File.pathSeparator))
urls.add(new File(".").toURL());
else
{
urls.add(new File(s).toURL());
if (tok.hasMoreTokens())
{
// Skip the separator.
tok.nextToken();
// If the classpath ended with a separator,
// append the current directory.
if (! tok.hasMoreTokens())
urls.add(new File(".").toURL());
}
}
}
}
catch (MalformedURLException ex)
{
System.err.println("Malformed path '" + s + "' in classpath '"
+ classPath + "'");
System.exit(1);
}
URL[] u = new URL[urls.size()];
for (int i = 0; i < u.length; i++)
{
u[i] = (URL) urls.get(i);
}
classLoader = new URLClassLoader(u, classLoader);
}
/**
* Loads the class with the given name (uses class path, if applicable)
*
* @param name the name of the class.
*/
public Class loadClass(String name)
{
ClassLoader loader = classLoader;
if (loader == null)
loader = Thread.currentThread().getContextClassLoader();
try
{
return loader.loadClass(name);
}
catch (ClassNotFoundException e)
{
System.err.println(name+" not found on "+loader);
System.exit(1);
// Unreacheable code.
return null;
}
}
/**
* Compile the given class (the instance of Remote), generating the stub and
* tie for it.
*
* @param remote
* the class to compile.
*/
public synchronized void compile(Class remote)
{
reset();
String s;
// Get the package.
s = remote.getName();
int p = s.lastIndexOf('.');
if (p < 0)
{
// Root package.
packag = "";
implName = name = s;
}
else
{
packag = s.substring(0, p);
implName = name = s.substring(p + 1);
}
name = convertStubName(name);
stubName = name;
vars.put("#name", name);
vars.put("#package", packag);
vars.put("#implName", implName);
if (verbose)
System.out.println("Package " + packag + ", name " + name + " impl "
+ implName);
// Get the implemented remotes.
Class[] interfaces = remote.getInterfaces();
for (int i = 0; i < interfaces.length; i++)
{
if (Remote.class.isAssignableFrom(interfaces[i]))
{
if (! interfaces[i].equals(Remote.class))
{
implementedRemotes.add(interfaces[i]);
}
}
}
vars.put("#idList", getIdList(implementedRemotes));
// Collect and process methods.
Iterator iter = implementedRemotes.iterator();
while (iter.hasNext())
{
Class c = (Class) iter.next();
Method[] m = c.getMethods();
// Check if throws RemoteException.
for (int i = 0; i < m.length; i++)
{
Class[] exc = m[i].getExceptionTypes();
boolean remEx = false;
for (int j = 0; j < exc.length; j++)
{
if (RemoteException.class.isAssignableFrom(exc[j]))
{
remEx = true;
break;
}
}
if (! remEx && !force)
throw new CompilationError(m[i].getName() + ", defined in "
+ c.getName()
+ ", does not throw "
+ RemoteException.class.getName());
AbstractMethodGenerator mm = createMethodGenerator(m[i]);
methods.add(mm);
}
}
}
/**
* Create the method generator for the given method.
*
* @param m the method
*
* @return the created method generator
*/
protected AbstractMethodGenerator createMethodGenerator(Method m)
{
return new MethodGenerator(m, this);
}
/**
* Get the name of the given class. The class is added to imports, if not
* already present and not from java.lang and not from the current package.
*
* @param nameIt
* the class to name
* @return the name of class as it should appear in java language
*/
public String name(Class nameIt)
{
if (nameIt.isArray())
{
// Mesure dimensions:
int dimension = 0;
Class finalComponent = nameIt;
while (finalComponent.isArray())
{
finalComponent = finalComponent.getComponentType();
dimension++;
}
StringBuffer brackets = new StringBuffer();
for (int i = 0; i < dimension; i++)
{
brackets.append("[]");
}
return name(finalComponent) + " " + brackets;
}
else
{
String n = nameIt.getName();
if (! nameIt.isArray() && ! nameIt.isPrimitive())
if (! n.startsWith("java.lang")
&& ! (packag != null && n.startsWith(packag)))
extraImports.add(n);
int p = n.lastIndexOf('.');
if (p < 0)
return n;
else
return n.substring(p + 1);
}
}
/**
* Get the RMI-style repository Id for the given class.
*
* @param c
* the interface, for that the repository Id must be created.
* @return the repository id
*/
public String getId(Class c)
{
return "RMI:" + c.getName() + ":0000000000000000";
}
/**
* Get repository Id string array declaration.
*
* @param remotes
* the collection of interfaces
* @return the fully formatted string array.
*/
public String getIdList(Collection remotes)
{
StringBuffer b = new StringBuffer();
// Keep the Ids sorted, ensuring, that the same order will be preserved
// between compilations.
TreeSet sortedIds = new TreeSet();
Iterator iter = remotes.iterator();
while (iter.hasNext())
{
sortedIds.add(getId((Class) iter.next()));
}
iter = sortedIds.iterator();
while (iter.hasNext())
{
b.append(" \"" + iter.next() + "\"");
if (iter.hasNext())
b.append(", \n");
}
return b.toString();
}
/**
* Generate stub. Can only be called from {@link #compile}.
*
* @return the string, containing the text of the generated stub.
*/
public String generateStub()
{
String template = getResource("Stub.jav");
// Generate methods.
StringBuffer b = new StringBuffer();
Iterator iter = methods.iterator();
while (iter.hasNext())
{
AbstractMethodGenerator m = (AbstractMethodGenerator) iter.next();
b.append(m.generateStubMethod());
}
vars.put("#stub_methods", b.toString());
vars.put("#imports", getImportStatements());
vars.put("#interfaces", getAllInterfaces());
String output = replaceAll(template, vars);
return output;
}
/**
* Get the list of all interfaces, implemented by the class, that are
* derived from Remote.
*
* @return the string - all interfaces.
*/
public String getAllInterfaces()
{
StringBuffer b = new StringBuffer();
Iterator iter = implementedRemotes.iterator();
while (iter.hasNext())
{
b.append(name((Class) iter.next()));
if (iter.hasNext())
b.append(", ");
}
return b.toString();
}
/**
* Generate Tie. Can only be called from {@link #compile}.
*
* @return the string, containing the text of the generated Tie.
*/
public String generateTie()
{
String template;
if (poaMode)
template = getResource("Tie.jav");
else
template = getResource("ImplTie.jav");
// Generate methods.
HashFinder hashFinder = new HashFinder();
// Find the hash character position:
Iterator iter = methods.iterator();
String[] names = new String[methods.size()];
int p = 0;
for (int i = 0; i < names.length; i++)
names[i] = ((MethodGenerator) iter.next()).getGiopMethodName();
int hashCharPosition = hashFinder.findHashCharPosition(names);
iter = methods.iterator();
while (iter.hasNext())
((MethodGenerator) iter.next()).hashCharPosition = hashCharPosition;
vars.put("#hashCharPos", Integer.toString(hashCharPosition));
ArrayList sortedMethods = new ArrayList(methods);
Collections.sort(sortedMethods, this);
iter = sortedMethods.iterator();
StringBuffer b = new StringBuffer();
MethodGenerator prev = null;
while (iter.hasNext())
{
MethodGenerator m = (MethodGenerator) iter.next();
m.previous = prev;
m.hashCharPosition = hashCharPosition;
prev = m;
b.append(m.generateTieMethod());
}
vars.put("#tie_methods", b.toString());
vars.put("#imports", getImportStatements());
String output = replaceAll(template, vars);
return output;
}
public int compare(Object a, Object b)
{
MethodGenerator g1 = (MethodGenerator) a;
MethodGenerator g2 = (MethodGenerator) b;
return g1.getHashChar() - g2.getHashChar();
}
/**
* Import the extra classes, used as the method parameters and return values.
*
* @return the additional import block.
*/
protected String getImportStatements()
{
TreeSet imp = new TreeSet();
Iterator it = extraImports.iterator();
while (it.hasNext())
{
String ic = it.next().toString();
imp.add("import " + ic + ";\n");
}
StringBuffer b = new StringBuffer();
it = imp.iterator();
while (it.hasNext())
{
b.append(it.next());
}
return b.toString();
}
/**
* If this flag is set (true by default), the compiler generates the Servant
* based classes. If set to false, the compiler generates the old style
* ObjectImpl based classes.
*/
public void setPoaMode(boolean mode)
{
poaMode = mode;
}
/**
* Set the verbose output mode (false by default)
*
* @param isVerbose the verbose output mode
*/
public void setVerbose(boolean isVerbose)
{
verbose = isVerbose;
}
/**
* If this flag is set (true by default), the compiler emits warnings.
*/
public void setWarnings(boolean warn)
{
warnings = warn;
}
/**
* Set the error ignore mode.
*/
public void setForce(boolean isforce)
{
force = isforce;
}
/**
* Get the package name.
*/
public String getPackageName()
{
return packag;
}
/**
* Get the proposed stub name
*/
public String getStubName()
{
return stubName;
}
/**
* Additional processing of the stub name.
*/
public String convertStubName(String name)
{
// Drop the Impl suffix, if one exists.
if (name.endsWith("Impl"))
return name.substring(0, name.length() - "Impl".length());
else
return name;
}
}