package tk.eclipse.plugin.htmleditor.assist; import java.io.StringReader; import java.util.ArrayList; import java.util.HashMap; import jp.aonir.fuzzyxml.FuzzyXMLAttribute; import jp.aonir.fuzzyxml.FuzzyXMLDocument; import jp.aonir.fuzzyxml.FuzzyXMLElement; import jp.aonir.fuzzyxml.FuzzyXMLNode; import jp.aonir.fuzzyxml.FuzzyXMLParser; import org.eclipse.core.resources.IFile; import org.objectstyle.wolips.wodclipse.core.completion.WodParserCache; import org.w3c.css.sac.AttributeCondition; import org.w3c.css.sac.Condition; import org.w3c.css.sac.ConditionalSelector; import org.w3c.css.sac.ElementSelector; import org.w3c.css.sac.InputSource; import org.w3c.css.sac.Selector; import org.w3c.css.sac.SelectorList; import org.w3c.css.sac.SimpleSelector; import org.w3c.dom.css.CSSRule; import org.w3c.dom.css.CSSRuleList; import org.w3c.dom.css.CSSStyleRule; import org.w3c.dom.css.CSSStyleSheet; import tk.eclipse.plugin.htmleditor.HTMLUtil; import com.steadystate.css.parser.CSSOMParser; /** * This provides code completion for class attribute of HTML tags. * <p> * Completion proposals is got from an internal stylesheet like following: * <pre> * <style type="text/css"> * ... * </style> * </pre> * And external stylesheet that is included as following: * <pre> * <link rel="stylesheet" type="text/css" href="..." /> * </pre> */ public class CSSAssistProcessor { private HashMap<String, ArrayList<String>> _rules = new HashMap<String, ArrayList<String>>(); private IFile _file; /** * Reload informations of code completion. * * @param source HTML */ public void reload(IFile file, String source) { this._file = file; _rules.clear(); source = HTMLUtil.scriptlet2space(source, false); FuzzyXMLDocument doc; if ("html".equalsIgnoreCase(file.getFileExtension())) { try { doc = WodParserCache.parser(file).getHtmlEntry().getModel(); } catch (Exception e) { e.printStackTrace(); doc = null; } } else { doc = new FuzzyXMLParser(false).parse(source); } if (doc != null) { processElement(doc.getDocumentElement()); } } private void processElement(FuzzyXMLElement element) { if (element.getName().equalsIgnoreCase("link")) { // external CSS cpecified by link tag String rel = ""; String type = ""; String href = ""; FuzzyXMLAttribute[] attrs = element.getAttributes(); for (int i = 0; i < attrs.length; i++) { if (attrs[i].getName().equalsIgnoreCase("rel")) { rel = attrs[i].getValue(); } else if (attrs[i].getName().equalsIgnoreCase("type")) { type = attrs[i].getValue(); } else if (attrs[i].getName().equalsIgnoreCase("href")) { href = attrs[i].getValue(); } } if (rel.equalsIgnoreCase("stylesheet") && type.equalsIgnoreCase("text/css")) { try { IFile css = getFile(href); if (css != null && css.exists()) { String text = new String(HTMLUtil.readStream(css.getContents())); processStylesheet(text); } } catch (Exception ex) { } } } else if (element.getName().equalsIgnoreCase("style")) { // inline CSS defined in style tag String type = ""; FuzzyXMLAttribute[] attrs = element.getAttributes(); for (int i = 0; i < attrs.length; i++) { if (attrs[i].getName().equalsIgnoreCase("type")) { type = attrs[i].getValue(); } } if (type.equalsIgnoreCase("text/css")) { String text = HTMLUtil.getXPathValue(element, "/"); processStylesheet(text); } } FuzzyXMLNode[] children = element.getChildren(); for (int i = 0; i < children.length; i++) { if (children[i] instanceof FuzzyXMLElement) { processElement((FuzzyXMLElement) children[i]); } } } private IFile getFile(String path) { if (path.startsWith("/")) { return null; // try { // HTMLProjectParams params = new HTMLProjectParams(file.getProject()); // return file.getProject().getFile(new Path(params.getRoot()).append(path)); // } catch(Exception ex){ // } } return _file.getProject().getFile(_file.getParent().getProjectRelativePath().append(path)); } /** * Parse CSS and create completion informations. * * @param css CSS */ private void processStylesheet(String css) { try { CSSOMParser parser = new CSSOMParser(); InputSource is = new InputSource(new StringReader(css)); CSSStyleSheet stylesheet = parser.parseStyleSheet(is); CSSRuleList list = stylesheet.getCssRules(); // ArrayList assists = new ArrayList(); for (int i = 0; i < list.getLength(); i++) { CSSRule rule = list.item(i); if (rule instanceof CSSStyleRule) { CSSStyleRule styleRule = (CSSStyleRule) rule; String selector = styleRule.getSelectorText(); SelectorList selectors = parser.parseSelectors(new InputSource(new StringReader(selector))); for (int j = 0; j < selectors.getLength(); j++) { Selector sel = selectors.item(j); if (sel instanceof ConditionalSelector) { Condition cond = ((ConditionalSelector) sel).getCondition(); SimpleSelector simple = ((ConditionalSelector) sel).getSimpleSelector(); if (simple instanceof ElementSelector) { String tagName = ((ElementSelector) simple).getLocalName(); if (tagName == null) { tagName = "*"; } else { tagName = tagName.toLowerCase(); } if (cond instanceof AttributeCondition) { AttributeCondition attrCond = (AttributeCondition) cond; if (_rules.get(tagName) == null) { ArrayList<String> classes = new ArrayList<String>(); // classes.add(new AssistInfo(attrCond.getValue())); classes.add(attrCond.getValue()); _rules.put(tagName, classes); } else { ArrayList<String> classes = _rules.get(tagName); // classes.add(new AssistInfo(attrCond.getValue())); classes.add(attrCond.getValue()); } } } } } } } } catch (Throwable ex) { // java.lang.Error: Missing return statement in function } } /** * Returns completion proposal for class attribute. * * @param tagName a tag name * @return an array of completion proposals */ public AssistInfo[] getAssistInfo(String tagName, String value) { try { if (value.indexOf(' ') != -1) { value = value.substring(0, value.lastIndexOf(' ') + 1); } else { value = ""; } ArrayList<String> assists = new ArrayList<String>(); ArrayList<String> all = _rules.get("*"); if (all != null) { assists.addAll(all); } if (_rules.get(tagName.toLowerCase()) != null) { ArrayList<String> list = _rules.get(tagName.toLowerCase()); assists.addAll(list); } AssistInfo[] info = new AssistInfo[assists.size()]; for (int i = 0; i < assists.size(); i++) { String keyword = assists.get(i); info[i] = new AssistInfo(value + keyword, keyword); } return info; } catch (Exception ex) { } return new AssistInfo[0]; } }