Created
March 11, 2015 17:42
-
-
Save nidefawl/8b17837edad281235046 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
(c) SIB Visions 2012 | |
http://www.sibvisions.com | |
*/ | |
public class SimpleSourceFile { | |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
// Class members | |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
/** the source parser. */ | |
private ASTParser parser; | |
/** the parsed tokens of the source file. */ | |
private CompilationUnit cuSource; | |
/** the original document. */ | |
private Document docOrig; | |
/** the class type info. */ | |
private TypeDeclaration tdClass; | |
/** the javadoc of the class. */ | |
private JavadocInfo jdoc; | |
/** the full qualified name of the class. */ | |
private String sClassName; | |
/** the full qualified name of the super class after loading. */ | |
private String sLoadedSuperClass; | |
/** the full qualified name of the parent class. */ | |
private String sSuperClass; | |
/** whether the source is changed. */ | |
private boolean bChanged = false; | |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
// Initialization | |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
/** | |
* Creates a new instance of <code>SimpleSourceFile</code> without source | |
* information. | |
*/ | |
public SimpleSourceFile() { | |
load(""); | |
} | |
/** | |
* Creates a new instance of <code>SimpleSourceFile</code> with a given | |
* source file. | |
* | |
* @param pFile the java source file | |
* @throws IOException if an error occurs during reading the file | |
*/ | |
public SimpleSourceFile(File pFile) throws IOException { | |
if (pFile.exists()) { | |
JavaSource source = JavaSource.get(pFile.getAbsolutePath()); | |
if (source != null) { | |
synchronized(source) { | |
load(new String(FileUtil.getContent(pFile))); | |
} | |
} else { | |
load(new String(FileUtil.getContent(pFile))); | |
} | |
} else { | |
load(""); | |
} | |
} | |
/** | |
* Creates a new instance of <code>SimpleSourceFile</code> with a given | |
* source stream. The stream is closed after reading | |
* | |
* @param pStream the java source stream | |
* @throws IOException if an error occurs during reading the stream | |
*/ | |
public SimpleSourceFile(InputStream pStream) throws IOException { | |
this(pStream, true); | |
} | |
/** | |
* Creates a new instance of <code>SimpleSourceFile</code> with a given | |
* source stream. | |
* | |
* @param pStream the java source stream | |
* @param pAutoClose whether the stream should be closed after reading | |
* @throws IOException if an error occurs during reading the stream | |
*/ | |
public SimpleSourceFile(InputStream pStream, boolean pAutoClose) throws IOException { | |
load(new String(FileUtil.getContent(pStream, pAutoClose))); | |
} | |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
// User-defined methods | |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
/** | |
* Loads the properties from the source file. | |
* | |
* @param pSource the java source | |
*/@SuppressWarnings({ | |
"unchecked", "rawtypes" | |
}) | |
private void load(String pSource) { | |
jdoc = new JavadocInfo(); | |
if (parser == null) { | |
parser = ASTUtil.createParser(); | |
} | |
parser.setSource(pSource.toCharArray()); | |
cuSource = (CompilationUnit) parser.createAST(null); | |
if (docOrig != null) { | |
docOrig.set(pSource); | |
} else { | |
docOrig = new Document(pSource); | |
} | |
//we support only one class per file | |
List types = cuSource.types(); | |
if (types.size() > 0 && types.get(0) instanceof TypeDeclaration) { | |
tdClass = (TypeDeclaration) types.get(0); | |
String sPackage; | |
//we have to use the current package, if set | |
if (cuSource.getPackage() != null) { | |
sPackage = cuSource.getPackage().getName().getFullyQualifiedName() + "."; | |
} else { | |
sPackage = ""; | |
} | |
sClassName = sPackage + tdClass.getName().getFullyQualifiedName(); | |
if (tdClass.getSuperclassType() != null) { | |
String sSuperName = ((SimpleType)(tdClass.getSuperclassType())).getName().getFullyQualifiedName(); | |
if (sSuperName.indexOf('.') > 0) { | |
sSuperClass = sSuperName; | |
} else { | |
//try to detect the full qualified name of the super class through the imports | |
List < ImportDeclaration > liImports = cuSource.imports(); | |
boolean bFound = false; | |
if (liImports != null) { | |
String sTempName = "." + sSuperName; | |
String sImport; | |
for (int i = 0, anz = liImports.size(); !bFound && i < anz; i++) { | |
sImport = liImports.get(i).getName().getFullyQualifiedName(); | |
if (sImport.endsWith(sTempName)) { | |
bFound = true; | |
sSuperClass = liImports.get(i).getName().getFullyQualifiedName(); | |
} | |
} | |
} | |
if (!bFound) { | |
//we use the current package | |
sSuperClass = sPackage + sSuperName; | |
} | |
} | |
//cache for optimizing imports in apply | |
sLoadedSuperClass = sSuperClass; | |
} | |
jdoc = new JavadocInfo(tdClass.getJavadoc(), pSource); | |
} else { | |
tdClass = null; | |
} | |
//currently loaded file can not be changed! | |
bChanged = false; | |
} | |
/** | |
* Sets the comment for the class. | |
* | |
* @param pText the comment | |
*/ | |
public void setClassComment(String pText) { | |
bChanged |= !CommonUtil.equals(pText, jdoc.getDescription()); | |
jdoc.setDescription(pText); | |
} | |
/** | |
* Gets the comment for the class. | |
* | |
* @return the comment | |
*/ | |
public String getClassComment() { | |
return jdoc.getDescription(); | |
} | |
/** | |
* Sets the full qualified name of the class. | |
* | |
* @param pName the class name | |
*/ | |
public void setClassName(String pName) { | |
if (pName == null) { | |
throw new IllegalArgumentException("It is not allowed to remove the class definition (class name = null)!"); | |
} | |
bChanged |= !CommonUtil.equals(pName, sClassName); | |
sClassName = pName; | |
} | |
/** | |
* Gets the full qualified name of the class. | |
* | |
* @return the class name | |
*/ | |
public String getClassName() { | |
return sClassName; | |
} | |
/** | |
* Sets the full qualified class name of the parent class. | |
* | |
* @param pSuperClass the full qualified class name of the parent class | |
*/ | |
public void setSuperClass(String pSuperClass) { | |
bChanged |= !CommonUtil.equals(pSuperClass, sSuperClass); | |
sSuperClass = pSuperClass; | |
} | |
/** | |
* Gets the full qualified class name of the parent class. | |
* | |
* @return the class name | |
*/ | |
public String getSuperClass() { | |
return sSuperClass; | |
} | |
/** | |
* Replaces the relevant information with the specific values | |
* and saves the source file. | |
* | |
* @throws Exception if the source modification fails | |
*/@SuppressWarnings("unchecked") | |
protected void apply() throws Exception { | |
if (!bChanged) { | |
return; | |
} | |
AST ast = cuSource.getAST(); | |
ASTRewrite arw = ASTRewrite.create(ast); | |
//---------------------------------------------------- | |
// Change Package, Classname and Constructor | |
//---------------------------------------------------- | |
String sCurrentPackage; | |
//set package | |
String[] sClassInfo = ClassUtil.splitName(sClassName); | |
if (sClassInfo[0] != null) { | |
if (cuSource.getPackage() != null) { | |
arw.set(cuSource.getPackage(), PackageDeclaration.NAME_PROPERTY, ast.newName(sClassInfo[0]), null); | |
sCurrentPackage = sClassInfo[0]; | |
} else { | |
PackageDeclaration pdcl = ast.newPackageDeclaration(); | |
pdcl.setName(ast.newName(sClassInfo[0])); | |
arw.set(cuSource, CompilationUnit.PACKAGE_PROPERTY, pdcl, null); | |
sCurrentPackage = sClassInfo[0]; | |
} | |
} else { | |
arw.set(cuSource, CompilationUnit.PACKAGE_PROPERTY, null, null); | |
sCurrentPackage = null; | |
} | |
if (tdClass == null) { | |
//new class | |
tdClass = ast.newTypeDeclaration(); | |
tdClass.setName(ast.newSimpleName(sClassInfo[1])); | |
ListRewrite lrwImp = arw.getListRewrite(cuSource, CompilationUnit.TYPES_PROPERTY); | |
lrwImp.insertFirst(tdClass, null); | |
} else { | |
//change class name | |
SimpleName snClass = ast.newSimpleName(sClassInfo[1]); | |
arw.set(tdClass, TypeDeclaration.NAME_PROPERTY, snClass, null); | |
//chage constructors | |
MethodDeclaration[] methods = tdClass.getMethods(); | |
if (methods != null) { | |
for (MethodDeclaration method: methods) { | |
if (method.isConstructor()) { | |
arw.set(method, MethodDeclaration.NAME_PROPERTY, snClass, null); | |
} | |
} | |
} | |
} | |
//change Superclass | |
List < ImportDeclaration > liImports = cuSource.imports(); | |
//remove "old" import | |
if (sLoadedSuperClass != null) { | |
if (liImports != null) { | |
boolean bFound = false; | |
for (int i = liImports.size() - 1; !bFound && i >= 0; i--) { | |
if (sLoadedSuperClass.equals(liImports.get(i).getName().getFullyQualifiedName())) { | |
bFound = true; | |
arw.remove(liImports.get(i), null); | |
liImports.remove(i); | |
} | |
} | |
} | |
} | |
if (sSuperClass == null) { | |
arw.set(tdClass, TypeDeclaration.SUPERCLASS_TYPE_PROPERTY, null, null); | |
} else { | |
//check new super class import | |
String[] sSuperClassInfo = ClassUtil.splitName(sSuperClass); | |
if (sSuperClassInfo[0] != null) { | |
boolean bFound = false; | |
if (sCurrentPackage != null) { | |
bFound = sSuperClassInfo[0].equals(sCurrentPackage); | |
} | |
if (!bFound) { | |
if (liImports != null) { | |
for (int i = 0, anz = liImports.size(); !bFound && i < anz; i++) { | |
bFound = sSuperClass.equals(liImports.get(i).getName().getFullyQualifiedName()); | |
} | |
} | |
} | |
//class not already importet -> add import | |
if (!bFound) { | |
ImportDeclaration idSuper = ast.newImportDeclaration(); | |
idSuper.setName(ast.newName(sSuperClass)); | |
ListRewrite lrwImp = arw.getListRewrite(cuSource, CompilationUnit.IMPORTS_PROPERTY); | |
lrwImp.insertLast(idSuper, null); | |
} | |
} | |
arw.set(tdClass, TypeDeclaration.SUPERCLASS_TYPE_PROPERTY, ast.newSimpleType(ast.newName(sSuperClassInfo[1])), null); | |
} | |
//---------------------------------------------------- | |
// Change Javadoc of the class | |
//---------------------------------------------------- | |
if ("".equals(jdoc.toString())) { | |
//no javadoc | |
arw.set(tdClass, TypeDeclaration.JAVADOC_PROPERTY, null, null); | |
} else { | |
arw.set(tdClass, TypeDeclaration.JAVADOC_PROPERTY, jdoc.createJavadoc(tdClass.getAST()), null); | |
} | |
//---------------------------------------------------- | |
// Modify AST | |
//---------------------------------------------------- | |
TextEdit edit = arw.rewriteAST(docOrig, null); | |
edit.apply(docOrig); | |
ASTUtil.applyFormat(docOrig); | |
//simple reparse (important for the comments) | |
parser.setSource(docOrig.get().toCharArray()); | |
cuSource = (CompilationUnit) parser.createAST(null); | |
//---------------------------------------------------- | |
// Modify comments | |
//---------------------------------------------------- | |
//Class comment after the last bracket | |
int iCorr = 0; | |
List < Comment > liComments = cuSource.getCommentList(); | |
if (liComments != null) { | |
String sOldName = tdClass.getName().getFullyQualifiedName(); | |
String sText; | |
for (Comment com: liComments) { | |
sText = docOrig.get(com.getStartPosition() - iCorr, com.getLength()); | |
int iPos = 0; | |
int iClassLen = sClassInfo[1].length(); | |
int iLenDiff = sOldName.length() - iClassLen; | |
int iNextPos; | |
CharacterType ctypePrev; | |
CharacterType ctypeNext; | |
while ((iPos = sText.indexOf(sOldName, iPos)) >= 0) { | |
if (iPos > 0) { | |
ctypePrev = StringUtil.getCharacterType("" + sText.charAt(iPos - 1)); | |
} else { | |
ctypePrev = CharacterType.OnlySpecial; | |
} | |
iNextPos = iPos + sOldName.length(); | |
if (iNextPos <= sText.length() - 1) { | |
ctypeNext = StringUtil.getCharacterType("" + sText.charAt(iNextPos)); | |
} else { | |
ctypeNext = CharacterType.OnlySpecial; | |
} | |
//don't replace word parts e.g. ContactsWorkScreen should not be replaced with Contacts | |
if ((ctypePrev == CharacterType.OnlySpecial || ctypePrev == CharacterType.OnlyWhitespace) && (ctypeNext == CharacterType.OnlySpecial || ctypeNext == CharacterType.OnlyWhitespace)) { | |
docOrig.replace(com.getStartPosition() + iPos - iCorr, sOldName.length(), sClassInfo[1]); | |
iCorr += iLenDiff; | |
iPos += iClassLen; | |
} else { | |
iPos = iNextPos; | |
} | |
} | |
} | |
} | |
//---------------------------------------------------- | |
// Update | |
//---------------------------------------------------- | |
load(docOrig.get()); | |
bChanged = false; | |
} | |
/** | |
* Stores the changed source into the given file. | |
* | |
* @param pFile the target file for the source | |
* @throws Exception if an error occurs during saving | |
*/ | |
public void saveAs(File pFile) throws Exception { | |
apply(); | |
JavaSource source = JavaSource.get(pFile.getAbsolutePath()); | |
if (source != null) { | |
synchronized(source) { | |
FileUtil.save(pFile, docOrig.get().getBytes()); | |
source.reload(); | |
} | |
} else { | |
FileUtil.save(pFile, docOrig.get().getBytes()); | |
} | |
} | |
/** | |
* Stores the changed source into the given stream. | |
* | |
* @param pStream the target stream for the source | |
* @throws Exception if an error occurs during saving | |
*/ | |
public void saveAs(OutputStream pStream) throws Exception { | |
apply(); | |
pStream.write(docOrig.get().getBytes()); | |
pStream.flush(); | |
} | |
/** | |
* Returns the modified source code. | |
* | |
* @return the modified source code | |
* @throws Exception if the source modification fails | |
*/ | |
protected String getSource() throws Exception { | |
apply(); | |
return docOrig.get(); | |
} | |
} // SimpleSourceFile |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment