/*
* Copyright (C) 2000 - 2012 TagServlet Ltd
*
* This file is part of Open BlueDragon (OpenBD) CFML Server Engine.
*
* OpenBD is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* Free Software Foundation,version 3.
*
* OpenBD 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 OpenBD. If not, see http://www.gnu.org/licenses/
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this Program, or any covered work, by linking or combining
* it with any of the JARS listed in the README.txt (or a modified version of
* (that library), containing parts covered by the terms of that JAR, the
* licensors of this Program grant you additional permission to convey the
* resulting work.
* README.txt @ http://www.openbluedragon.org/license/README.txt
*
* http://openbd.org/
* $Id: DynamicWebServiceTypeGenerator.java 2147 2012-07-02 01:57:34Z alan $
*/
/*
* Created on Jan 22, 2005
*
* To change the template for this generated file go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
package com.naryx.tagfusion.cfm.xml.ws.dynws;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.beans.SimpleBeanInfo;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.io.Writer;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import org.apache.axis.AxisProperties;
import org.apache.axis.MessageContext;
import org.apache.axis.components.compiler.Compiler;
import org.apache.axis.components.compiler.CompilerError;
import org.apache.axis.components.compiler.CompilerFactory;
import com.nary.util.FastMap;
import com.nary.util.UUID;
import com.nary.util.string;
import com.naryx.tagfusion.cfm.engine.cfEngine;
import com.naryx.tagfusion.cfm.engine.cfWebServices;
import com.naryx.tagfusion.cfm.xml.ws.encoding.ser.IComplexObject;
public class DynamicWebServiceTypeGenerator {
public static final String NAMESPACE = "na_svr";
private static final String EOL = System.getProperty("line.separator");
private String javaCacheDir;
private String dynDirName;
private Map<String, CFCSourceInfo> srcMap;
private Map<String, String> srcBeanInfoMap;
private List<DynamicCacheClassLoader> clList;
private Map<String, String> typeList;
private CFCDescriptor desc;
public DynamicWebServiceTypeGenerator(String javaCache) {
this.javaCacheDir = javaCache;
}
public String generateType(CFCDescriptor d, MessageContext msgContext) throws IOException {
Map<String, CFCSourceInfo> sMap = new FastMap<String, CFCSourceInfo>();
Map<String, String> sBeanInfoMap = new FastMap<String, String>();
List<DynamicCacheClassLoader> clList = new ArrayList<DynamicCacheClassLoader>();
String name = UUID.generateKey();
// Populate the necessary lists
String dynClsName = prepareType(d, name, new FastMap<String, String>(false), sMap, sBeanInfoMap, clList);
// Build the classes (if necessary)
if (buildClasses(name, sMap, sBeanInfoMap, clList)) {
// Link the DynamicCacheClassLoaders together (as dependents)
linkClassLoaders(getClassLoader(dynClsName));
}
// Return the class name
return dynClsName;
}
public String prepareType(CFCDescriptor d, String dynName, Map<String, String> typeNames, Map<String, CFCSourceInfo> srcMap, Map<String, String> srcBeanInfoMap, List<DynamicCacheClassLoader> clList) {
this.dynDirName = dynName;
this.srcMap = srcMap;
this.srcBeanInfoMap = srcBeanInfoMap;
this.clList = clList;
this.typeList = typeNames;
String fqName = getFQName(d.getName());
this.typeList.put(fqName, fqName);
this.desc = d;
return buildType();
}
public String getKnownType(String fullTypeName) {
return this.typeList.get(fullTypeName);
}
public void addType(CFCDescriptor d) {
DynamicWebServiceTypeGenerator g = new DynamicWebServiceTypeGenerator(this.javaCacheDir);
g.prepareType(d, this.dynDirName, this.typeList, this.srcMap, this.srcBeanInfoMap, this.clList);
}
public static String getFQName(String cfcName) {
String rtn = NAMESPACE + "." + cfcName.trim();
String name = rtn.substring(rtn.lastIndexOf(".") + 1);
rtn = rtn.substring(0, rtn.lastIndexOf("."));
name = (Character.isDigit(name.charAt(0)) ? "n" + name : name);
rtn = prefixNSDigits(rtn);
return rtn + "." + name;
}
private String buildType() {
// Create the full class name
String name = getFQName(this.desc.getName());
name = name.replaceAll("-", "");
// Look for the generated type first
DynamicCacheClassLoader cl = null;
Class<?> cls = DynamicCacheClassLoader.findLoadedClass(name, DynamicCacheClassLoader.SKEL_CLASSES);
if (cls != null)
cl = (DynamicCacheClassLoader) cls.getClassLoader();
// If it's been generated already, get the class path to include.
// Otherwise, generate the java source code directly.
if (cl == null)
genSource();
else
this.clList.add(cl);
// Return the class name
return name;
}
private void genSource() {
// Determine the package name for the generated source
String prefix = this.desc.getName().trim();
String pkgName = NAMESPACE;
if (prefix.lastIndexOf(".") != -1) {
prefix = prefix.substring(0, prefix.lastIndexOf("."));
prefix = prefixNSDigits(prefix);
pkgName += "." + prefix;
pkgName = pkgName.replaceAll("-", "");
}
// Determine the class name for the generated source
String className = this.desc.getName().trim().substring(this.desc.getName().trim().lastIndexOf(".") + 1);
if (Character.isDigit(className.charAt(0)))
className = "n" + className;
// Start the java source
StringBuilder buffy = startClass(pkgName, className);
// Add WS methods for each CFC function
addOperations(buffy);
// Add WS properties for each CFC property
addProperties(buffy);
// Add the IComplexObject interface.
addIComplexObjectInterface(buffy);
// End the java source
endClass(buffy);
// Add the java source
this.srcMap.put(pkgName + "." + className, new CFCSourceInfo(buffy.toString(), this.desc.getFile(), this.desc.getName(), pkgName + "." + className));
// Start the BeanInfo source
buffy = startBeanInfoClass(pkgName, className + "BeanInfo");
// Add WS properties for each CFC property
addBeanInfoProperties(pkgName + "." + className, buffy);
// End the java source
endClass(buffy);
// Add the BeanInfo source
this.srcBeanInfoMap.put(pkgName + "." + className + "BeanInfo", buffy.toString());
}
private StringBuilder startClass(String pkgName, String clsName) {
StringBuilder buffy = new StringBuilder();
buffy.append("/* This java file was dynamically generated by OpenBlueDragon */" + EOL);
buffy.append("package " + pkgName + ";" + EOL);
buffy.append(EOL);
buffy.append("public class " + clsName + " implements ");
buffy.append(Serializable.class.getName() + ", " + IComplexObject.class.getName() );
buffy.append(" {" + EOL);
return buffy;
}
private StringBuilder startBeanInfoClass(String pkgName, String clsName) {
StringBuilder buffy = new StringBuilder();
buffy.append("/* This java file was dynamically generated by OpenBlueDragon */" + EOL);
buffy.append("package " + pkgName + ";" + EOL);
buffy.append(EOL);
buffy.append("public class " + clsName + " extends ");
buffy.append(SimpleBeanInfo.class.getName());
buffy.append(" {" + EOL);
return buffy;
}
private void endClass(StringBuilder buffy) {
buffy.append("/* End of dynamically generated class. */" + EOL);
buffy.append("}" + EOL);
}
private void addOperations(StringBuilder buffy) {
for (int i = 0; i < this.desc.getFunctionCount(); i++) {
String rtnType = desc.getFunctionReturnType(i, this);
String rtnTypeKlass = null;
if (rtnType == null) {
rtnType = "void";
rtnTypeKlass = "null";
} else {
rtnTypeKlass = rtnType + ".class";
}
buffy.append(" public " + rtnType + " " + desc.getFunctionName(i) + "(");
for (int x = 0; x < this.desc.getFunctionParameterCount(i); x++) {
if (x > 0)
buffy.append(", ");
buffy.append(this.desc.getFunctionParameterType(i, x, this) + " " + this.desc.getFunctionParameterName(i, x));
}
buffy.append(") throws java.lang.Exception {" + EOL);
// Have all operations look for a CFCInvoker in the Request context.
// Then use it to do the actual invocation.
buffy.append(" " + CFCInvoker.class.getName() + " cfcInvoker = " + CFCInvoker.class.getName() + ".getCFCInvoker(Thread.currentThread());" + EOL);
buffy.append(" if (cfcInvoker == null)" + EOL);
buffy.append(" throw new Exception(\"Cannot find a CFCInvoker for current thread: \" + Thread.currentThread() + \". Perhaps " + desc.getFunctionName(i) + " is being invoked a second time.\");" + EOL);
buffy.append(" Class rtnType = " + rtnTypeKlass + ";" + EOL);
buffy.append(" String[] argNames = new String[" + this.desc.getFunctionParameterCount(i) + "];" + EOL);
buffy.append(" Object[] argValues = new Object[" + this.desc.getFunctionParameterCount(i) + "];" + EOL);
for (int x = 0; x < this.desc.getFunctionParameterCount(i); x++) {
buffy.append(" argNames[" + x + "] = \"" + this.desc.getFunctionParameterName(i, x) + "\";" + EOL);
buffy.append(" argValues[" + x + "] = " + this.desc.getFunctionParameterName(i, x) + ";" + EOL);
}
if (rtnType.equals("void"))
buffy.append(" cfcInvoker.invoke(\"" + this.desc.getFunctionName(i) + "\", argNames, argValues, rtnType, this.getClass().getClassLoader());" + EOL);
else
buffy.append(" return (" + rtnType + ")cfcInvoker.invoke(\"" + this.desc.getFunctionName(i) + "\", argNames, argValues, rtnType, this.getClass().getClassLoader());" + EOL);
buffy.append(" }" + EOL);
buffy.append(EOL);
}
}
private void addProperties(StringBuilder buffy) {
for (int i = 0; i < this.desc.getPropertyCount(); i++) {
String typ = this.desc.getPropertyType(i, this);
String name = this.desc.getPropertyName(i);
buffy.append(" private " + typ + " " + name + ";" + EOL);
buffy.append(" public " + typ + " get" + name + "(){" + EOL);
buffy.append(" return " + name + ";" + EOL);
buffy.append(" }" + EOL);
buffy.append(" public void set" + name + "(" + typ + " pVal){" + EOL);
buffy.append(" " + name + "=pVal;" + EOL);
buffy.append(" }" + EOL);
}
buffy.append(EOL);
}
private void addBeanInfoProperties(String fullClassName, StringBuilder buffy) {
buffy.append(" public " + PropertyDescriptor.class.getName() + "[] getPropertyDescriptors(){" + EOL);
if (this.desc.getPropertyCount() > 0) {
buffy.append(" try{" + EOL);
buffy.append(" " + PropertyDescriptor.class.getName() + "[] rtn;" + EOL);
buffy.append(" rtn = new " + PropertyDescriptor.class.getName() + "[" + this.desc.getPropertyCount() + "];" + EOL);
for (int i = 0; i < this.desc.getPropertyCount(); i++) {
String name = this.desc.getPropertyName(i);
String readMethodName = "get" + name;
String writeMethodName = "set" + name;
buffy.append(" rtn[" + i + "] = new " + PropertyDescriptor.class.getName() + "(\"" + name + "\", " + fullClassName + ".class, \"" + readMethodName + "\", \"" + writeMethodName + "\");" + EOL);
}
buffy.append(" return rtn;" + EOL);
buffy.append(" }" + EOL);
buffy.append(" catch (" + IntrospectionException.class.getName() + " ex) {" + EOL);
buffy.append(" throw new " + RuntimeException.class.getName() + "(\"Cannot create PropertyDescriptor for " + fullClassName + ". \" + ex.getMessage(), ex);" + EOL);
buffy.append(" }" + EOL);
} else {
buffy.append(" return null;" + EOL);
}
buffy.append(" }" + EOL);
}
private void addIComplexObjectInterface(StringBuilder buffy) {
// Add the bd_setFieldValues method.
buffy.append(" public void bd_setFieldValues(");
buffy.append(Map.class.getName() + " data, " + List.class.getName() + " missingRequiredFieldNames){" + EOL);
// Now one for each defined property
for (int i = 0; i < desc.getPropertyCount(); i++) {
/*
* Add some code that looks like this:
*
* if (data.containsKey("field1")) this.field1 =
* (MyFieldClass)data.get("field1");
*
* Notice that this implementation doesn't add any names to the
* missingRequiredFieldNames List. That's because this is the server side
* IComplexObject. In practice this method should never actually be called
* (i.e. the conversion routines should never have a need to populate an
* instance of this Type).
*/
buffy.append(" if (data.containsKey(\"" + desc.getPropertyName(i) + "\"))" + EOL);
buffy.append(" this." + desc.getPropertyName(i) + " = (" + this.desc.getPropertyType(i, this) + ")data.get(\"" + desc.getPropertyName(i) + "\");" + EOL);
}
buffy.append(" }" + EOL);
buffy.append(EOL);
// Add the bd_getFieldValues method.
buffy.append(" public void bd_getFieldValues(" + Map.class.getName() + " data){" + EOL);
// Now one for each defined property
for (int i = 0; i < desc.getPropertyCount(); i++) {
/*
* Add some code that looks like this.
*
* data.put("field1", this.field1);
*/
buffy.append(" data.put(\"" + desc.getPropertyName(i) + "\", this." + desc.getPropertyName(i) + ");" + EOL);
}
buffy.append(" }" + EOL);
buffy.append(EOL);
// Add the bd_getFieldTypes method.
buffy.append(" public void bd_getFieldTypes(" + Map.class.getName() + " data){" + EOL);
// Now one for each defined property
for (int i = 0; i < desc.getPropertyCount(); i++) {
/*
* Add some code that looks like this.
*
* data.put("field1", MyFieldClass.class);
*/
buffy.append(" data.put(\"" + desc.getPropertyName(i) + "\", " + desc.getPropertyType(i, this) + ".class);" + EOL);
}
buffy.append(" }" + EOL);
buffy.append(EOL);
// Add the bd_getCfcName method.
buffy.append(" public String bd_getCfcName(){" + EOL);
/*
* Add some code that looks like this.
*
* return "MyCfcPackage.MyCfcName";
*/
buffy.append(" return \"" + desc.getName() + "\";" + EOL);
buffy.append(" }" + EOL);
buffy.append(EOL);
}
private boolean buildClasses(String name, Map<String, CFCSourceInfo> sMap, Map<String, String> sBeanInfoMap, List<DynamicCacheClassLoader> clList) throws IOException {
if (sMap.size() > 0) {
// Setup the classpath
StringBuilder buffy = getDefaultClasspath();
Iterator<DynamicCacheClassLoader> itr = clList.iterator();
while (itr.hasNext())
buffy.append(File.pathSeparator + itr.next().getCacheDir());
// Write the files
File rootDir = new File(genClassPath(name));
File[] srcFiles = new File[sMap.size() + sBeanInfoMap.size()];
CFCSourceInfo[] cfcInfo = new CFCSourceInfo[sMap.size()];
Iterator<String> keys = sMap.keySet().iterator();
for (int i = 0; keys.hasNext(); i++) {
String tmp = keys.next();
srcFiles[i] = writeFile(rootDir, tmp, sMap.get(tmp).source);
// Keep the underlying cfc file reference, name, and IComplexObject
// impl class name handy
cfcInfo[i] = sMap.get(tmp);
}
keys = sBeanInfoMap.keySet().iterator();
for (int i = sMap.size(); keys.hasNext(); i++) {
String tmp = keys.next();
srcFiles[i] = writeFile(rootDir, tmp, sBeanInfoMap.get(tmp));
}
// Compile the src
Compiler compiler = CompilerFactory.getCompiler();
compiler.setClasspath(buffy.toString());
compiler.setDestination(rootDir.getAbsolutePath());
for (int i = 0; i < srcFiles.length; i++) {
compiler.addFile(srcFiles[i].getAbsolutePath());
}
boolean result = compiler.compile();
// Problem encountered
if (!result) {
for (int i = 0; i < srcFiles.length; i++)
new File(srcFiles[i].getAbsolutePath().substring(0, srcFiles[i].getAbsolutePath().length() - 5) + ".class").delete();
// Build compile errors
StringBuilder message = new StringBuilder("Error compiling: " + EOL);
for (int i = 0; i < srcFiles.length; i++)
message.append(srcFiles[i].getAbsolutePath() + EOL);
message.append(":" + EOL);
List<CompilerError> errors = compiler.getErrors();
int count = errors.size();
for (int i = 0; i < count; i++) {
CompilerError error = errors.get(i);
if (i > 0)
message.append(EOL);
message.append("Line ");
message.append(error.getStartLine());
message.append(", column ");
message.append(error.getStartColumn());
message.append(": ");
message.append(error.getMessage());
}
message.append(EOL + "Classpath: " + EOL);
message.append(buffy.toString() + EOL);
throw new IOException("Server compileError: " + EOL + message.toString());
}
// Delete the temporary *.java file and check return code
// for (int i=0; i<srcFiles.length; i++)
// srcFiles[i].delete();
// Add an addition to the class path for this new cache
DynamicCacheClassLoader dcl = DynamicCacheClassLoaderFactory.newClassLoader(rootDir.getCanonicalPath(), DynamicCacheClassLoader.SKEL_CLASSES);
clList.add(dcl);
// Associate the generated classes with their cfc file
// equivalents for this DynamicCacheClassLoader
for (int i = 0; i < cfcInfo.length; i++) {
dcl.associateCFC(cfcInfo[i].file);
dcl.setIComplexObject(cfcInfo[i].name, cfcInfo[i].impl);
}
// Return true, we did create a new DynamicCacheClassLoader
return true;
}
// Return false, we had no source to compile
return false;
}
/**
* Writes the specified source out to a physical file in the rootDir. Returns
* the File that was written.
*
* @param rootDir
* root directory in which to write the files
* @param name
* name of the file to write
* @param source
* source of the file to write
* @return File created on disk
* @throws IOException
*/
private File writeFile(File rootDir, String name, String source) throws IOException {
File f = new File(rootDir, name.replace('.', File.separatorChar) + ".java");
if (!f.getParentFile().exists())
f.getParentFile().mkdirs();
f.createNewFile();
if (!f.exists() && f.getAbsolutePath().length() < 256)
throw new IOException("Could not create file: " + f.getAbsolutePath());
// Write out the file
Writer fw = null;
try {
fw = cfEngine.thisPlatform.getFileIO().getFileWriter(f);
fw.write(source);
fw.flush();
} finally {
if (fw != null)
fw.close();
}
return f;
}
/**
* Link together the dependent DynamicCacheClassLoaders as associated
* instances to the primary DynamicCacheClassLoader (the one responsible for
* the type/class being generated). All DynamicCacheClassLoaders in the list
* at this point are needed to realize the requested type/class.
*
* @param primaryCl
*/
private void linkClassLoaders(DynamicCacheClassLoader primaryCl) {
for (int i = 0; i < this.clList.size(); i++)
primaryCl.associateDynamicCacheClassLoader(this.clList.get(i));
}
public DynamicCacheClassLoader getClassLoader(String clsName) throws IOException {
Class<?> dynClass = DynamicCacheClassLoader.findLoadedClass(clsName, DynamicCacheClassLoader.SKEL_CLASSES);
if (dynClass == null)
throw new IOException("Dynamic class: " + clsName + " not found. Perhaps there was an error compiling it.");
return (DynamicCacheClassLoader) dynClass.getClassLoader();
}
private String genClassPath(String file) {
File dir = new File(this.javaCacheDir);
return new File(dir, file).getAbsolutePath();
}
private StringBuilder getDefaultClasspath() throws IOException {
StringBuilder classpath = new StringBuilder();
ClassLoader cl = Thread.currentThread().getContextClassLoader();
while (cl != null) {
if (cl instanceof URLClassLoader) {
URL[] urls = ((URLClassLoader) cl).getURLs();
for (int i = 0; (urls != null) && i < urls.length; i++)
addPathToBuffer(urls[i], classpath);
}
cl = cl.getParent();
}
// Add the BD JARs to the java compiler classpath
String ps = System.getProperty("path.separator");
String fs = System.getProperty("file.separator");
String dir = null;
String libDir = null;
String bootClassPath = AxisProperties.getProperty("sun.boot.class.path");
// The JARs are in the WEB-INF lib folder.
dir = cfWebServices.getDocRootDir();
if (dir != null && !dir.endsWith(fs))
dir += fs;
dir = dir + "WEB-INF" + fs;
libDir = dir + "lib" + fs;
String altLibDir = cfEngine.getAltLibPath();
if (!bootClassPath.contains("webservices.jar") && classpath.indexOf("webservices.jar") < 0) {
classpath.append( ps );
classpath.append( getJarPath(libDir, altLibDir, "webservices.jar") );
}
if (!bootClassPath.contains("wsdl4j.jar") && classpath.indexOf("wsdl4j.jar") < 0) {
classpath.append( ps );
classpath.append( getJarPath(libDir, altLibDir, "wsdl4j.jar") );
}
if (!bootClassPath.contains("saaj.jar") && classpath.indexOf("saaj.jar") < 0) {
classpath.append( ps );
classpath.append( getJarPath(libDir, altLibDir, "saaj.jar") );
}
if (!bootClassPath.contains("jaxrpc.jar") && classpath.indexOf("jaxrpc.jar") < 0) {
classpath.append( ps );
classpath.append( getJarPath(libDir, altLibDir, "jaxrpc.jar") );
}
classpath.append(ps + dir + "classes");
// Add the J2EE specific jars (not required as classes, may be in the classes dir)
classpath.append( ps );
classpath.append( getJarPath(libDir, altLibDir, "OpenBlueDragon.jar") );
// boot classpath isn't found in above search
if (bootClassPath != null)
classpath.append(ps + bootClassPath);
return classpath;
}
public static String getJarPath(String libDir, String altPath, String jarfile ) throws IOException {
File f = new File( libDir + jarfile );
if ( f.exists() )
return f.getAbsolutePath();
if ( altPath != null ){
f = new File( altPath + jarfile );
if ( f.exists() )
return f.getAbsolutePath();
}
throw new IOException("JAR: " + jarfile + " was not found in either [" + libDir + "] or [" + altPath + "]");
}
@SuppressWarnings("deprecation")
private void addPathToBuffer(URL url, StringBuilder classpath) {
String path = url.getPath();
// The path is URL encoded so we need to URL decode it
// before adding it to the classpath.
path = java.net.URLDecoder.decode(path);
// If it is a drive letter, adjust accordingly.
if (path.length() >= 3 && path.charAt(0) == '/' && path.charAt(2) == ':')
path = path.substring(1);
classpath.append(path);
classpath.append(File.pathSeparatorChar);
// if its a jar extract Class-Path entries from manifest
File file = new File(url.getFile());
if (file.isFile()) {
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
if (isJar(fis)) {
JarFile jar = new JarFile(file);
Manifest manifest = jar.getManifest();
if (manifest != null) {
Attributes attributes = manifest.getMainAttributes();
if (attributes != null) {
String s = attributes.getValue(java.util.jar.Attributes.Name.CLASS_PATH);
String base = file.getParent();
if (s != null) {
List<String> tokens = string.split(s, " ");
for (int i = 0; i < tokens.size(); i++) {
String t = tokens.get(i).toString();
classpath.append(base + File.separatorChar + t);
classpath.append(File.pathSeparatorChar);
}
}
}
}
}
} catch (IOException ioe) {
if (fis != null) {
try {
fis.close();
} catch (IOException ioe2) {
}
}
}
}
}
// an exception or emptiness signifies not a jar
public static boolean isJar(InputStream is) {
JarInputStream jis= null;
try {
jis = new JarInputStream(is);
if (jis.getNextEntry() != null) {
return true;
}
} catch (IOException ioe) {
}finally{
org.aw20.io.StreamUtil.closeStream(jis);
}
return false;
}
public static String prefixNSDigits(String str) {
if (str != null) {
str = str.trim();
StringBuilder buffy = new StringBuilder();
List<String> tokens = string.split(str, ".");
for (int i = 0; i < tokens.size(); i++) {
String t = tokens.get(i).toString();
if (Character.isDigit(t.charAt(0)))
t = "ns" + t;
buffy.append("." + t);
}
if (str.endsWith("."))
buffy.append(".");
str = (str.startsWith(".") ? buffy.toString() : buffy.toString().substring(1));
}
return str;
}
private class CFCSourceInfo {
public String source = null;
public String name = null;
public String impl = null;
public File file = null;
public CFCSourceInfo(String javaSource, File file, String name, String impl) {
this.source = javaSource;
this.file = file;
this.name = name;
this.impl = impl;
}
}
}