package org.tessell.generators.views; import java.util.ArrayList; import java.util.List; import java.util.Stack; import org.apache.commons.lang.StringUtils; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import com.google.gwt.dom.client.Element; /** A SAX handler for ui.xml files. */ class UiXmlHandler extends DefaultHandler { final List<UiWithDeclaration> withFields = new ArrayList<UiWithDeclaration>(); // ui:field elements + ui:field widgets + anonymous widgets final List<UiFieldDeclaration> uiFields = new ArrayList<UiFieldDeclaration>(); final List<UiStyleDeclaration> styleFields = new ArrayList<UiStyleDeclaration>(); final Stack<String> parentUiFieldOperation = new Stack<String>(); final Stack<Boolean> popParentUiFieldName = new Stack<Boolean>(); String firstTagType; private UiStyleDeclaration lastStyle; private int anonymousIndex; @Override public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { boolean doParentPop = false; if (firstTagType == null && uri.startsWith("urn:import")) { firstTagType = StringUtils.substringAfterLast(uri, ":") + "." + localName; } final int indexOfUiField = attributes.getIndex("urn:ui:com.google.gwt.uibinder", "field"); boolean hasUpperCase = !localName.toLowerCase().equals(localName); if (uri.equals("urn:ui:com.google.gwt.uibinder") && localName.equals("with")) { // ui:with final String type = attributes.getValue(attributes.getIndex("type")); final String name = attributes.getValue(attributes.getIndex("field")); withFields.add(new UiWithDeclaration(type, name)); } else if (uri.equals("urn:ui:com.google.gwt.uibinder") && localName.equals("style")) { // ui:style int fieldIndex = attributes.getIndex("field"); int typeIndex = attributes.getIndex("type"); if (typeIndex > -1) { final String name = fieldIndex == -1 ? "style" : attributes.getValue(fieldIndex); final String type = attributes.getValue(typeIndex); lastStyle = new UiStyleDeclaration(type, name); styleFields.add(lastStyle); } } else if (uri.equals("")) { // elements, only pick up elements with ui:fields if (indexOfUiField > -1) { final String name = attributes.getValue(indexOfUiField); uiFields.add(new UiFieldDeclaration(Element.class.getName(), name, null)); } } else if (!uri.equals("") && !uri.equals("urn:ui:com.google.gwt.uibinder") && !hasUpperCase) { // probably (hopefully) a UiChild. Unfortunately, this is wild, random guess because // we a) we have to reinvent UiBinder's lookup strategy, and b) even worse, because Tessell's // codegen runs at buildtime, we can't rely on .class files or reflection being available for // every single type mentioned in a ui.xml file. So, instead we just hope the convention holds. String operation = "add" + localName.substring(0, 1).toUpperCase() + localName.substring(1); parentUiFieldOperation.push(getParentOperationOrNull().split("\\.")[0] + "." + operation); doParentPop = true; } else if (!uri.equals("") && !uri.equals("urn:ui:com.google.gwt.uibinder") && hasUpperCase) { // all other widgets final String name; if (indexOfUiField == -1) { name = "_anonymous" + (anonymousIndex++); } else { name = attributes.getValue(indexOfUiField); } final String type = (StringUtils.substringAfterLast(uri, ":") + "." + localName) // .replace("com.google.gwt.user.client.ui", "org.tessell.gwt.user.client.ui"); // use the subclasses uiFields.add(new UiFieldDeclaration(type, name, getParentOperationOrNull())); parentUiFieldOperation.push(name + ".add"); doParentPop = true; } popParentUiFieldName.push(doParentPop); } @Override public void characters(char[] ch, int start, int length) { if (lastStyle != null) { lastStyle.css += new String(ch, start, length); } } @Override public void endElement(String namespaceURI, String localName, String qName) { lastStyle = null; boolean doParentPop = popParentUiFieldName.pop(); if (doParentPop) { parentUiFieldOperation.pop(); } } private String getParentOperationOrNull() { if (parentUiFieldOperation.isEmpty()) { return null; } else { return parentUiFieldOperation.peek(); } } List<UiFieldDeclaration> uiFieldWidgets() { List<UiFieldDeclaration> widgets = new ArrayList<UiFieldDeclaration>(); for (UiFieldDeclaration uiField : uiFields) { if (!uiField.type.equals(Element.class.getName())) { widgets.add(uiField); } } return widgets; } }