
net.jangaroo.extxml.file.ExtComponentSrcFileScanner Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ext-xml Show documentation
Show all versions of ext-xml Show documentation
Allows a declarative description of UI components
The newest version!
package net.jangaroo.extxml.file;
import net.jangaroo.extxml.model.ComponentClass;
import net.jangaroo.extxml.model.ComponentSuite;
import net.jangaroo.extxml.model.ComponentType;
import net.jangaroo.extxml.model.ConfigAttribute;
import net.jangaroo.extxml.model.DescriptionHolder;
import net.jangaroo.utils.log.Log;
import net.jangaroo.extxml.util.FileScanner;
import net.jangaroo.extxml.util.Rule;
import net.jangaroo.extxml.util.TidyComment;
import org.codehaus.plexus.util.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* A FileScanner to read Ext JS component classes (JavaScript or ActionScript) into a {@link net.jangaroo.extxml.model.ComponentSuite}.
*/
public final class ExtComponentSrcFileScanner {
private ExtComponentSrcFileScanner() {
//hide the constructor
}
public static void scan(ComponentSuite componentSuite, File srcFile, ComponentType type) throws IOException {
State state = new State(componentSuite, srcFile);
if (ComponentType.ActionScript.equals(type)) {
String className = FileUtils.removeExtension(srcFile.getName());
state.addClass(className);
state.cc.setType(ComponentType.ActionScript);
Log.d(String.format("Parsing AS3 class '%s'", className));
EXT_COMPONENT_AS_FILE_SCANNER.scan(srcFile, state);
} else if (ComponentType.JavaScript.equals(type)) {
EXT_COMPONENT_SRC_FILE_SCANNER.scan(srcFile, state);
if (state.cc != null) {
state.cc.setType(ComponentType.JavaScript);
}
}
state.end();
}
// Rules used in both scanners:
private static final Rule TYPE_RULE = new Rule("@[apx]type\\s+([\\p{Alpha}$_.][\\p{Alnum}$_.]*)") {
public void matched(State state, List groups) {
if (state.insideComment) {
state.setXtype(groups.get(0), state.cc.getFullClassName());
}
}
};
private static final Rule CFG_RULE = new Rule("@cfg\\s+[{]?([\\p{Alnum}$_./|]+)[}]?\\s+([\\p{Alnum}$_]+)(.*)$") {
public void matched(State state, List groups) {
if (state.insideComment) {
// use List#remove(0) to skip optional type if missing:
state.addCfg(groups.size() == 3 ? groups.remove(0) : "*", groups.remove(0), groups.remove(0));
}
}
};
private static final Rule COMMENT_START_RULE = new Rule("^\\s*/\\*\\* ?(.*)$") {
public void matched(State state, List groups) {
if (!state.insideComment) {
state.startComment(groups.get(0));
}
}
};
private static final Rule COMMENT_RULE = new Rule("^\\s*\\*? ?(.*)$") {
// inside comment: skip leading white space before first '*':
public void matched(State state, List groups) {
if (state.insideComment) {
state.addComment(groups.get(0));
}
}
};
private static final Rule COMMENT_END_RULE = new Rule("\\*/") {
public void matched(State state, List groups) {
if (state.insideComment) {
state.endComment();
}
}
};
private final static FileScanner EXT_COMPONENT_AS_FILE_SCANNER = new FileScanner()
.add(new Rule("^\\s*package\\s+([\\p{Alnum}$_.]+)") {
public void matched(State state, List groups) {
if (!state.insideComment) {
state.cc.setFullClassName(groups.get(0) + "." + state.cc.getClassName());
// Next comment following the package declaration is the class comment:
state.setDescriptionHolder(state.cc);
}
}
})
.add(new Rule("^\\s*import\\s+([\\p{Alnum}$_.]+);") {
public void matched(State state, List groups) {
if (!state.insideComment) {
state.addImport(groups.get(0));
}
}
})
.add(new Rule("\\bextends\\s+([\\p{Alnum}$_.]+)") {
public void matched(State state, List groups) {
if (!state.insideComment) {
state.setExtends(groups.get(0));
}
}
})
.add(new Rule("(?:public\\s+static|static\\s+public)\\s+const\\s+[apx]type\\s*:\\s*String\\s*=\\s*['\"]([^'\"]+)['\"]") {
public void matched(State state, List groups) {
if (!state.insideComment) {
state.setXtype(groups.get(0), state.cc.getFullClassName());
}
}
})
.add(CFG_RULE)
.add(COMMENT_END_RULE)
.add(new Rule("^?\\s*/\\*\\*?(.*)$") {
public void matched(State state, List groups) {
if (!state.insideComment) {
state.startComment(groups.get(0));
}
}
})
.add(COMMENT_RULE);
private final static FileScanner EXT_COMPONENT_SRC_FILE_SCANNER = new FileScanner()
.add(new Rule("@class\\s+([\\p{Alnum}$_.]+)") {
public void matched(State state, List groups) {
if (state.insideComment) {
state.addClass(groups.get(0));
}
}
})
.add(new Rule("@extends\\s+([\\p{Alnum}$_.]+)") {
public void matched(State state, List groups) {
if (state.insideComment) {
state.setExtends(groups.get(0));
}
}
})
.add(new Rule("@constructor") {
public void matched(State state, List groups) {
if (state.insideComment) {
state.setDescriptionHolder(null); // assign collected description to class
}
}
})
.add(new Rule("\\bExt\\.reg\\('([\\p{Alnum}$_.]+)',\\s*([\\p{Alnum}$_.]+)\\);") {
public void matched(State state, List groups) {
if (!state.insideComment) {
// old-style xtype registration, still used e.g. in Ext.Component.js:
state.setXtype(groups.get(0), groups.get(1));
}
}
})
.add(new Rule("\\bExt\\.Container\\.LAYOUTS\\['([\\p{Alnum}$_.]+)'\\]\\s*=\\s*([\\p{Alnum}$_.]+);") {
public void matched(State state, List groups) {
if (!state.insideComment) {
// layout type registration using LAYOUTS['type']:
state.setXtype(groups.get(0) + "layout", groups.get(1));
}
}
})
.add(new Rule("\\bExt\\.Container\\.LAYOUTS\\.([\\p{Alnum}$_.]+)\\s*=\\s*([\\p{Alnum}$_.]+);") {
public void matched(State state, List groups) {
if (!state.insideComment) {
// layout type registration using LAYOUTS.type:
state.setXtype(groups.get(0) + "layout", groups.get(1));
}
}
})
.add(TYPE_RULE)
.add(CFG_RULE)
.add(COMMENT_END_RULE)
.add(COMMENT_START_RULE)
.add(COMMENT_RULE);
private static class State {
private final static String[] SPECIAL_EXT_CLASS_TO_XTYPE = {
"Ext.grid.Column", "gridcolumn",
"Ext.grid.BooleanColumn", "booleancolumn",
"Ext.grid.NumberColumn", "numbercolumn",
"Ext.grid.DateColumn", "datecolumn",
"Ext.grid.TemplateColumn", "templatecolumn",
"Ext.util.Observable", "observable",
"Ext.data.Field", "datafield",
// "abstract" layouts:
"Ext.layout.ContainerLayout", "containerlayout",
"Ext.layout.BoxLayout", "boxlayout",
// Ext.layout.Container.LAYOUTS setting comes too late for this class, so we have to assign it manually:
"Ext.layout.BorderLayout", "borderlayout",
// Ext's built-in Action class: use upper-case xtype so it does not clash with the attribute "action"!
"Ext.Action", "Action"
};
private ComponentSuite componentSuite;
private File srcFile;
private ComponentClass cc;
private boolean insideComment;
private StringBuilder description = new StringBuilder();
private DescriptionHolder descriptionHolder;
State(ComponentSuite componentSuite, File srcFile) {
this.componentSuite = componentSuite;
this.srcFile = srcFile;
}
private void addClass(String className) {
addIfHasXtype(cc);
cc = new ComponentClass(srcFile);
cc.setFullClassName(jsType2asType(className));
setDescriptionHolder(cc);
// special cases: xtypes for certain Ext classes that have implicit xtypes
// or provide config attributes for subclasses, or are added as EXML elements:
for (int i = 0; i < SPECIAL_EXT_CLASS_TO_XTYPE.length; i+=2) {
if (SPECIAL_EXT_CLASS_TO_XTYPE[i].equals(className)) {
cc.setXtype(SPECIAL_EXT_CLASS_TO_XTYPE[i+1]);
break;
}
}
}
private void addImport(String className) {
cc.addImport(className);
}
private void setExtends(String superClassName) {
if (isActionScript()) {
String fullClassName = cc.getPackageName() + "." + superClassName;
for (String imp : cc.getImports()) {
if (imp.endsWith(superClassName)) {
fullClassName = imp;
}
}
cc.setSuperClassName(fullClassName);
} else if (cc != null) {
cc.setSuperClassName(jsType2asType(superClassName));
}
}
public void setXtype(String xtype, String className) {
if (cc != null && cc.getXtype() == null && jsType2asType(className).equals(cc.getFullClassName())) {
cc.setXtype(xtype);
}
}
private void addCfg(String type, String name, String descriptionLine) {
ConfigAttribute configAttribute = new ConfigAttribute(name, type);
cc.addCfg(configAttribute);
setDescriptionHolder(configAttribute);
if (descriptionLine.length() > 0) { // suppress first empty line
description.append(descriptionLine).append('\n');
}
}
private void startComment(String comment) {
assert !insideComment;
insideComment = true;
addComment(comment);
}
private void endComment() {
assert insideComment;
setDescriptionHolder(null);
insideComment = false;
}
private void addComment(String comment) {
assert insideComment;
description.append(comment).append('\n');
}
void validateComponentClass(ComponentClass cc) {
if (cc != null) {
if (cc.getImports().isEmpty()) {
Log.w("No imports in Compontent class");
}
}
}
void end() {
addIfHasXtype(cc);
}
private String jsType2asType(String jsType) {
int lastDotPos = jsType.lastIndexOf('.');
return lastDotPos == -1
? jsType
: jsType.substring(0, lastDotPos).toLowerCase() + jsType.substring(lastDotPos);
}
private void addIfHasXtype(ComponentClass cc) {
if (cc != null) {
if (cc.getXtype() != null) {
validateComponentClass(cc);
componentSuite.addComponentClass(cc);
Log.i(String.format("Component class '%s' with xtype '%s' parsed.", cc.getFullClassName(), cc.getXtype()));
} else {
Log.d(String.format("Class '%s' has no xtype - skipped.", cc.getFullClassName()));
}
}
}
private void setDescriptionHolder(DescriptionHolder nextDescriptionHolder) {
if (nextDescriptionHolder != descriptionHolder) {
String cleanedDescription = TidyComment.tidy(description.toString()).trim();
if (descriptionHolder != null && cleanedDescription.length() > 0) {
descriptionHolder.setDescription(cleanedDescription);
}
description = new StringBuilder();
descriptionHolder = nextDescriptionHolder;
}
}
private boolean isActionScript() {
return srcFile.getName().lastIndexOf(".as") != -1;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy