package tk.eclipse.plugin.jspeditor.editors;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.apache.xerces.parsers.DOMParser;
import org.eclipse.jdt.core.IJavaProject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.InputSource;
import tk.eclipse.plugin.htmleditor.HTMLPlugin;
import tk.eclipse.plugin.htmleditor.assist.AttributeInfo;
import tk.eclipse.plugin.htmleditor.assist.TagInfo;
import tk.eclipse.plugin.htmleditor.gefutils.IJarVisitor;
import tk.eclipse.plugin.htmleditor.gefutils.JarAcceptor;
/**
*
* @author Naoki Takezoe
* @since 2.0.4
*/
public class TLDParser {
private IJavaProject project;
private String uri = null;
private String prefix = "";
private ArrayList<TagInfo> result = new ArrayList<TagInfo>();
/**
* Use the specified prefix.
*
* @param the prefix
*/
public TLDParser(IJavaProject project, String prefix){
this.project = project;
this.prefix = prefix;
}
/**
* Use tld's <code>shortname</code> (or <code>short-name</code>) as prefix.
*/
public TLDParser(IJavaProject project){
this(project, null);
}
/**
* Returns the <code>uri</code> which declared in the TLD.
*
* @return the <code>uri</code>
*/
public String getUri(){
return uri;
}
/**
* Returns parsed <code>List</code> which contains <code>TagInfo</code>.
*
* @return <code>List</code> which contains <code>TagInfo</code>
*/
public List<TagInfo> getResult(){
return result;
}
public void parse(InputStream in) throws Exception {
DOMParser parser = new DOMParser();
parser.setEntityResolver(new TLDResolver());
parser.parse(new InputSource(in));
Document doc = parser.getDocument();
Element element = doc.getDocumentElement();
NodeList nodeList = element.getChildNodes();
for(int i=0;i<nodeList.getLength();i++){
Node node = nodeList.item(i);
if(node instanceof Element){
Element childElement = (Element)nodeList.item(i);
String elementName = childElement.getNodeName();
if(elementName.equals("tag")){
result.add(parseTagElement(childElement));
} else if(elementName.equals("uri")){
uri = getChildText(childElement);
} else if(elementName.equals("shortname") || elementName.equals("short-name")){
if(prefix==null){
prefix = getChildText(childElement);
}
} else if(elementName.equals("tag-file")){
TagInfo tagInfo = parseTagFileElement(childElement);
if(tagInfo!=null){
result.add(tagInfo);
}
}
}
}
}
/**
* Parses the tag element and returns <code>TagInfo</code> as result.
*
* @param tagFile the tag element
* @return the <code>TagInfo</code> from the given tag
*/
private TagInfo parseTagElement(Element tag){
NodeList children = tag.getChildNodes();
List<AttributeInfo> attributes = new ArrayList<AttributeInfo>();
String name = null;
String description = "";
boolean hasBody = true;
for(int j=0;j<children.getLength();j++){
Node node = children.item(j);
if(node instanceof Element){
Element element = (Element)node;
String elementName = element.getNodeName();
if(elementName.equals("name")){
name = prefix + ":" + getChildText(element);
} else if(elementName.equals("bodycontent") || elementName.equals("body-content")){
hasBody = !getChildText(element).equals("empty");
} else if(elementName.equals("description")){
description = wrap(getChildText(element));
} else if(elementName.equals("attribute")){
AttributeInfo attrInfo = parseAttributeElement(element);
attributes.add(attrInfo);
}
}
}
TagInfo info = new TagInfo(name, hasBody);
info.setDescription(description);
for(int i=0;i<attributes.size();i++){
info.addAttributeInfo(attributes.get(i));
}
return info;
}
private AttributeInfo parseAttributeElement(Element attr){
NodeList children = attr.getChildNodes();
String name = null;
String description = "";
boolean required = false;
for(int i=0;i<children.getLength();i++){
Node node = children.item(i);
if(node instanceof Element){
Element element = (Element)node;
String elementName = element.getNodeName();
if(elementName.equals("name")){
name = getChildText(element);
} else if(elementName.equals("description")){
description = wrap(getChildText(element));
} else if(elementName.equals("required")){
if(getChildText(element).equals("true")){
required = true;
} else {
required = false;
}
}
}
}
AttributeInfo attrInfo = new AttributeInfo(name, true, AttributeInfo.NONE, required);
attrInfo.setDescription(description);
return attrInfo;
}
private static String getChildText(Element element){
StringBuffer sb = new StringBuffer();
NodeList children = element.getChildNodes();
for(int i=0;i<children.getLength();i++){
Node node = children.item(i);
if(node instanceof Text){
sb.append(node.getNodeValue());
}
}
return sb.toString().trim().replaceAll("\\s+", " ");
}
private static String wrap(String text){
StringBuffer sb = new StringBuffer();
int word = 0;
for(int i=0;i<text.length();i++){
char c = text.charAt(i);
if(word > 40){
if(c==' ' || c== '\t'){
sb.append('\n');
word = 0;
continue;
}
}
sb.append(c);
word++;
}
return sb.toString();
}
/**
* Parses the tag-file element and returns <code>TagInfo</code> as result.
*
* @since 2.0.5
* @param tagFile the tag-file element
* @return the <code>TagInfo</code> from given tag file
* or <code>null</code> if given tag file does not found.
*/
private TagInfo parseTagFileElement(Element tagFile) throws Exception {
NodeList children = tagFile.getChildNodes();
List<AttributeInfo> attributes = new ArrayList<AttributeInfo>();
String name = null;
String description = "";
boolean hasBody = true;
for(int j=0;j<children.getLength();j++){
Node node = children.item(j);
if(node instanceof Element){
Element element = (Element)node;
String elementName = element.getNodeName();
if(elementName.equals("name")){
name = prefix + ":" + getChildText(element);
} else if(elementName.equals("description")){
description = wrap(getChildText(element));
} else if(elementName.equals("path") && project!=null){
String path = getChildText(element);
InputStream in = getTagFile(path);
if(in != null){
TagInfo tagInfo = TagFileParser.parseTagFile(null, null, in);
for(int i=0;i<tagInfo.getAttributeInfo().length;i++){
attributes.add(tagInfo.getAttributeInfo()[i]);
}
} else {
return null;
}
}
}
}
TagInfo info = new TagInfo(name, hasBody);
info.setDescription(description);
for(int i=0;i<attributes.size();i++){
info.addAttributeInfo(attributes.get(i));
}
return info;
}
/**
* TODO This method might have to be moved to the utility class.
*
* @since 2.0.5
* @param path the path of the tag file
* <ul>
* <li>starts with "/META-INF/tags" : in the WAR file
* <li>starts with "/WEB-INF/tags" : in the JAR file
* </ul>
* @return the <code>InputSream</code> of the given tag file or <code>null</code>.
*/
private InputStream getTagFile(final String path){
try {
if(path.startsWith("/META-INF/tags")){
// in JAR (or classpath?)
return (InputStream)JarAcceptor.accept(project.getProject(),
new IJarVisitor(){
public Object visit(JarFile file, JarEntry entry) throws Exception {
if(("/" + entry.getName()).equals(path)){
return file.getInputStream(entry);
}
return null;
}
});
}
// in WAR
File basedir = TLDInfo.getBaseDir(project.getProject()).getLocation().makeAbsolute().toFile();
File file = new File(basedir, path);
return new FileInputStream(file);
} catch(Exception ex){
HTMLPlugin.logException(ex);
return null;
}
}
}