blob: da031ba5b8b56664d62889fc3394ce6abf5af99e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2010 Soyatec (http://www.soyatec.com) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Soyatec - initial API and implementation
*******************************************************************************/
package org.eclipse.xwt.tools.ui.designer.editor.model;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import org.eclipse.xwt.IConstants;
import org.eclipse.xwt.internal.xml.Attribute;
import org.eclipse.emf.common.util.EMap;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xwt.tools.ui.xaml.XamlAttribute;
import org.eclipse.xwt.tools.ui.xaml.XamlDocument;
import org.eclipse.xwt.tools.ui.xaml.XamlElement;
import org.eclipse.xwt.tools.ui.xaml.XamlFactory;
import org.eclipse.xwt.tools.ui.xaml.XamlNode;
/**
* Parse the XAML extension Markup: {StaticResource test} {StaticResource RessourceKey=test} {DynamicResource {x:Static SystemColors.ControlBrushKey}}
*
* @author jliu jin.liu@soyatec.com
*/
public class BraceHandler {
private XamlElement root;
private XamlNode current;
private XamlNode forParse;
private XamlDocument document;
public BraceHandler(XamlDocument document) {
this.document = document;
}
public XamlDocument getDocument() {
return document;
}
public XamlNode parse(XamlNode element, String text) {
this.forParse = element;
if (root != null) {
BraceHandler parser = new BraceHandler(document);
return parser.parse(element, text);
}
current = element;
StringTokenizer stringTokenizer = new StringTokenizer(text, "{}", true);
String previous = null;
String nextPrevious = null;
while (stringTokenizer.hasMoreTokens()) {
String token = stringTokenizer.nextToken();
if (previous != null) {
if (previous.equals("{")) {
if (token.equals("}")) {
// escape sequence
if (stringTokenizer.hasMoreTokens()) {
handleBlock(stringTokenizer.nextToken(" \t\n\r\f"), false);
}
} else {
startBlock();
}
} else if (previous.equals("}")) {
endBlock();
} else {
StringBuffer block = new StringBuffer(previous);
if (token.equals("{")) {
int level = 1;
block.append(token);
while (stringTokenizer.hasMoreTokens() && level >= 0) {
String value = stringTokenizer.nextToken();
if (value.equals("{")) {
level++;
} else if (value.equals("}")) {
level--;
}
if (level >= 0) {
block.append(value);
}
}
}
handleBlock(block.toString(),
(nextPrevious == null || !nextPrevious.equals("}")));
}
}
nextPrevious = previous;
previous = token;
}
XamlNode result = root;
root = null;
current = null;
return result;
}
protected void startBlock() {
}
protected void endBlock() {
if (current != null) {
EObject container = current.eContainer();
current = (XamlNode) container.eContainer();
}
}
/*
* (non-Javadoc)
* @see org.eclipse.xwt.tools.ui.designer.core.editor.builder.BraceHandler#expendNamespaces(org.eclipse.xwt.tools.ui.xaml.XamlNode, java.lang.String)
*/
protected String expendNamespaces(XamlNode element, String value) {
if (value.indexOf(':') == -1) {
return value;
}
EMap<String, String> declaredNamespaces = getDocument().getDeclaredNamespaces();
int length = IConstants.XAML_CLR_NAMESPACE_PROTO.length();
for (String prefix : declaredNamespaces.keySet()) {
String namespace = declaredNamespaces.get(prefix);
if (namespace.startsWith(IConstants.XAML_CLR_NAMESPACE_PROTO)) {
String packageName = namespace.substring(length);
value = value.replace(prefix + ":", packageName + '.');
}
}
return value;
}
/*
* (non-Javadoc)
* @see org.eclipse.xwt.tools.ui.designer.core.editor.builder.BraceHandler#handleContent(org.eclipse.xwt.tools.ui.xaml.XamlNode, java.lang.String)
*/
protected void handleContent(XamlNode element, String text) {
if (text.startsWith("{") && text.endsWith("}")) {
parse(element, text);
return;
} else {
// handle the case: <x:Array Type="ns:Type" >
if (IConstants.XAML_X_TYPE.equals(element.getName()) || IConstants.XAML_X_STATIC.equals(element.getName())) {
int index = text.indexOf(':');
if (index != -1) {
String ns = text.substring(0, index);
String content = text.substring(index + 1);
String namespace = getDocument().getDeclaredNamespace(ns);
if (namespace != null) {
XamlElement childElement = element.getChild(content, namespace);
if (childElement == null) {
childElement = XamlFactory.eINSTANCE.createElement(content, namespace);
childElement.getChildNodes().add(childElement);
}
return;
}
}
}
}
if (element instanceof Attribute && IConstants.XWT_X_NAMESPACE.equals(element.getNamespace())
&& IConstants.XAML_STYLE.equalsIgnoreCase(element.getName())) {
// handle the expansion of x:Style = "(j:class).variable"
text = expendNamespaces(element, text);
}
element.setValue(text);
}
protected XamlElement createElement(String token) {
int index = token.indexOf(':');
String namespace = null;
String name = token;
if (index != -1) {
String prefix = token.substring(0, index);
name = token.substring(index + 1);
namespace = document.getDeclaredNamespace(prefix);
}
if (namespace == null) {
namespace = document.getDeclaredNamespace(null);
}
XamlElement element = null;
if (current != null) {
element = current.getChild(name, namespace);
}
if (element == null) {
element = XamlFactory.eINSTANCE.createElement(name, namespace);
element.setId(XWTModelBuilder.generateID(name));
}
if (current != null && current != forParse) {
// There's no need to add the children for given parent at the beginning of parser.
current.getChildNodes().add(element);
} else {
if (root == null) {
root = element;
}
}
current = element;
return element;
}
private XamlAttribute createAttribute(XamlNode parent, String name, String namespace) {
XamlAttribute attribute = parent.getAttribute(name, namespace);
if (attribute == null) {
attribute = XamlFactory.eINSTANCE.createAttribute(name, namespace);
attribute.setId(XWTModelBuilder.generateID(name));
}
return attribute;
}
protected void handleBlock(String content, boolean newElement) {
String rootPattern = " \t\n\r\f=,";
StringTokenizer tokenizer = new StringTokenizer(content, rootPattern, true);
String attributeName = null;
String attributeValue = null;
boolean equals = false;
XamlElement element = null;
if (!newElement && current instanceof XamlElement) {
element = (XamlElement) current;
}
boolean skip = false;
String token = null;
while (skip || tokenizer.hasMoreTokens()) {
if (!skip) {
token = tokenizer.nextToken(rootPattern).trim();
}
skip = false;
if (token.length() == 0) {
continue;
}
if (element == null) {
element = createElement(token);
} else {
if (token.equals("=")) {
equals = true;
if ("xpath".equalsIgnoreCase(attributeName)) {
attributeValue = tokenizer.nextToken(",");
}
continue;
}
if (token.equals(",")) {
if (attributeName != null) {
if (attributeValue != null) {
XamlAttribute attribute = createAttribute(element, attributeName, element.getNamespace());
if ("path".equalsIgnoreCase(attributeName) && "Binding".equalsIgnoreCase(element.getName())) {
attributeValue = expendNamespaces(element, attributeValue);
}
handleContent(attribute, attributeValue);
if (!element.getAttributes().contains(attribute)) {
element.getAttributes().add(attribute);
}
current = attribute;
} else {
element.setValue(attributeName);
}
attributeName = null;
attributeValue = null;
equals = false;
}
} else {
if (attributeName == null) {
attributeName = token;
} else {
// String block = token;
StringBuffer block = new StringBuffer(token);
if (token.startsWith("{")) {
int level = 1;
while (tokenizer.hasMoreTokens() && level > 0) {
String value = tokenizer.nextToken("{}");
if (value.equals("{")) {
level++;
} else if (value.equals("}")) {
level--;
}
block.append(value);
}
}
attributeValue = block.toString();
try {
token = tokenizer.nextToken(rootPattern).trim();
skip = true;
continue;
} catch (NoSuchElementException e) {
}
}
}
}
skip = false;
}
if (equals) {
XamlAttribute attribute = createAttribute(element, attributeName, element.getNamespace());
if ("path".equalsIgnoreCase(attributeName) && "Binding".equalsIgnoreCase(element.getName())) {
attributeValue = expendNamespaces(element, attributeValue);
}
if (attributeValue != null) {
handleContent(attribute, attributeValue);
} else {
current = attribute;
}
if (!element.getAttributes().contains(attribute)) {
element.getAttributes().add(attribute);
}
} else if (attributeName != null) {
int index = attributeName.indexOf(":");
if (index != -1) {
element = createElement(attributeName);
current = (XamlNode) current.eContainer();
} else {
current.setValue(attributeName);
}
}
}
}