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; } } }