/*******************************************************************************
* Copyright (c) 2012 Olivier Moises
*
* 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:
* Olivier Moises- initial API and implementation
*******************************************************************************/
package org.eclipse.wazaabi.engine.core.themes.annotation.managers;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;
import org.eclipse.wazaabi.engine.core.annotations.managers.AnnotationManager;
import org.eclipse.wazaabi.engine.core.editparts.AbstractWidgetEditPart;
import org.eclipse.wazaabi.engine.edp.EDPUtils;
import org.eclipse.wazaabi.engine.edp.coderesolution.ICodeLocator;
import org.eclipse.wazaabi.mm.core.annotations.Annotation;
import org.eclipse.wazaabi.mm.core.annotations.AnnotationContent;
import org.eclipse.wazaabi.mm.core.styles.CoreStylesPackage;
import org.eclipse.wazaabi.mm.core.styles.StyleRule;
import org.eclipse.wazaabi.mm.core.themes.Themes.Theme;
import org.eclipse.wazaabi.mm.core.widgets.Container;
import org.eclipse.wazaabi.mm.core.widgets.Widget;
import org.eclipse.wazaabi.mm.edp.events.Event;
import org.eclipse.wazaabi.mm.edp.events.PathEvent;
import org.eclipse.wazaabi.mm.edp.handlers.EventHandler;
import org.eclipse.wazaabi.mm.edp.handlers.Parameter;
import org.eclipse.wazaabi.mm.edp.handlers.StringParameter;
public class ThemeDeclarationAnnotationManager extends AnnotationManager {
public static final String CORE_THEMES_ANNOTATION_SOURCE = "http://www.wazaabi.org/core/themes"; //$NON-NLS-1$
public static final String INLINE_KEY = "inline"; //$NON-NLS-1$
public static final String URI_KEY = "uri"; //$NON-NLS-1$
public static final String CLASS_KEY = "class"; //$NON-NLS-1$
public static final String VARIABLE_KEY = "variable"; //$NON-NLS-1$
private List<Widget> widgetDefinitions = new ArrayList<Widget>();
private HashMap<String, Widget> classDefinitions = new HashMap<String, Widget>();
public ThemeDeclarationAnnotationManager(Annotation annotation) {
super(annotation);
}
public void processAnnotation(AbstractWidgetEditPart host) {
if (host == null)
return;
Widget widget = (Widget) host.getModel();
parseDeclarations(host);
applyTheme(widget);
}
protected void parseDeclarations(AbstractWidgetEditPart host) {
assert getAnnotation() != null;
for (AnnotationContent content : getAnnotation().getContents()) {
if (INLINE_KEY.equals(content.getKey()))
parseTheme(parseInline(content.getValue()));
else if (URI_KEY.equals(content.getKey()))
parseTheme(parseURI(host, content.getValue()));
}
}
protected void parseTheme(Theme theme) {
if (theme == null)
return;
// TODO : we should merge class and widget definition instead of
// overwriting each time
for (Widget widget : theme.getChildren()) {
String classValue = getCoreThemeClassDeclaration(widget);
if (classValue != null && !"".equals(classValue)) //$NON-NLS-1$
classDefinitions.put(classValue, widget);
else
widgetDefinitions.add(widget);
}
}
public static String getCoreThemeClassDeclaration(Widget widget) {
for (Annotation annotation : widget.getAnnotations())
if (CORE_THEMES_ANNOTATION_SOURCE.equals(annotation.getSource()))
for (AnnotationContent annotationContent : annotation
.getContents())
if (CLASS_KEY.equals(annotationContent.getKey()))
return annotationContent.getValue();
return null;
}
protected void applyTheme(Widget target) {
HashMap<String, Object> variables = getVariables(target);
for (Annotation annotation : target.getAnnotations())
if (CORE_THEMES_ANNOTATION_SOURCE.equals(annotation.getSource()))
for (AnnotationContent content : annotation.getContents())
if (CLASS_KEY.equals(content.getKey()))
processClassDeclaration(target, content.getValue(),
variables);
List<Widget> widgetsToApply = resolveWidgetsToApply(target);
for (Widget w : widgetsToApply)
applyThemeOnWidget(w, target, variables);
if (target instanceof Container)
for (Widget child : ((Container) target).getChildren())
applyTheme(child);
}
protected Theme parseInline(String inlineValue) {
Resource resource = new XMIResourceImpl();
try {
resource.load(
new ByteArrayInputStream(inlineValue.getBytes("UTF-8")),
null);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (resource.getContents().get(0) instanceof Theme)
return (Theme) resource.getContents().get(0);
return null;
}
protected Theme parseURI(AbstractWidgetEditPart host, String uri) {
try {
String baseURI = host.getViewer().getCodeLocatorBaseUri();
if (baseURI != null && baseURI.length() != 0)
uri = EDPUtils.normalizeURI(baseURI, uri);
ICodeLocator codeLocator = (ICodeLocator) host.getViewer()
.getFactoryFor(null, uri, null, ICodeLocator.class);
if (codeLocator != null) {
InputStream in = codeLocator.getResourceInputStream(uri);
if (in != null) {
Resource resource = new XMIResourceImpl();
try {
resource.load(in, null);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (resource.getContents().get(0) instanceof Theme)
return (Theme) resource.getContents().get(0);
return null;
}
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
// TODO : use case 'BlankWidget' not supported at the moment
protected Widget resolveWidgetToApply(Widget widget, String className) {
Widget w = classDefinitions.get(className);
if (w != null && w.getClass().isAssignableFrom(widget.getClass()))
return w;
return null;
}
// TODO : use case 'BlankWidget' not supported at the moment
protected List<Widget> resolveWidgetsToApply(Widget widget) {
List<Widget> result = new ArrayList<Widget>();
for (Widget w : widgetDefinitions)
if (w.getClass().isAssignableFrom(widget.getClass()))
result.add(w);
return result;
}
protected void processClassDeclaration(Widget target, String className,
HashMap<String, Object> variables) {
if (className == null || "".equals(className)) //$NON-NLS-1$
return;
Widget widgetToApply = resolveWidgetToApply(target, className);
if (widgetToApply != null)
applyThemeOnWidget(widgetToApply, target, variables);
}
protected void applyThemeOnWidget(Widget themedWidget, Widget uiWidget,
HashMap<String, Object> variables) {
// First we process EventHandlers
for (EventHandler eventHandler : themedWidget.getHandlers()) {
EventHandler clone = (EventHandler) EcoreUtil.copy(eventHandler);
replaceVariables(clone, uiWidget, variables);
uiWidget.getHandlers().add(clone);
}
// styleRules
for (StyleRule rule : themedWidget.getStyleRules()) {
StyleRule uiRule = null;
for (StyleRule _uiRule : uiWidget.getStyleRules()) {
if (rule.getPropertyName().equals(_uiRule.getPropertyName())
&& rule.getClass().equals(_uiRule.getClass())) {
uiRule = _uiRule;
break;
}
}
if (uiRule == null) {
StyleRule newRule = (StyleRule) EcoreUtil.copy(rule);
uiWidget.getStyleRules().add(0, newRule);
} else {
// TODO : some tests are recurrent
for (EStructuralFeature feature : rule.eClass()
.getEAllStructuralFeatures()) {
boolean isSetWithDefaultValue = true;
if (feature != CoreStylesPackage.Literals.STYLE_RULE__PROPERTY_NAME
&& !feature.isMany()
&& !feature.isTransient()
&& feature.isChangeable() && !feature.isVolatile()) {
if (feature.getDefaultValue() != null)
isSetWithDefaultValue = feature.getDefaultValue()
.equals(rule.eGet(feature));
else
isSetWithDefaultValue = (rule.eGet(feature) == null);
if (!isSetWithDefaultValue
&& ((feature.getDefaultValue() != null && feature
.getDefaultValue().equals(
uiRule.eGet(feature))) || feature
.getDefaultValue() == null
&& uiRule.eGet(feature) == null)) {
if ((rule.eGet(feature) != null && !rule.eGet(
feature).equals(uiWidget.eGet(feature)))
|| (rule.eGet(feature) == null && uiWidget
.eGet(feature) != null)) {
uiRule.eSet(feature, rule.eGet(feature));
}
}
}
}
}
}
}
protected void replaceVariables(EventHandler eventHandler,
Widget destination, HashMap<String, Object> variables) {
for (Parameter parameter : eventHandler.getParameters()) {
if (parameter instanceof StringParameter) {
String variableName = getVariableName(((StringParameter) parameter)
.getValue());
if (variableName != null && !"".equals(variableName)) //$NON-NLS-1$
((StringParameter) parameter).setValue((String) variables
.get(variableName));
}
}
for (Event event : eventHandler.getEvents()) {
if (event instanceof PathEvent) {
String variableName = getVariableName(((PathEvent) event)
.getPath());
if (variableName != null && !"".equals(variableName)) //$NON-NLS-1$
((PathEvent) event).setPath((String) variables
.get(variableName));
}
}
}
protected String getVariableName(String str) {
if (str != null && !"".equals(str) && str.startsWith("${")
&& str.endsWith("}") && str.length() > 2)
return str.substring(2, str.length() - 1);
return null;
}
protected HashMap<String, Object> getVariables(Widget widget) {
HashMap<String, Object> variables = new HashMap<String, Object>();
for (Annotation annotation : widget.getAnnotations())
if (CORE_THEMES_ANNOTATION_SOURCE.equals(annotation.getSource()))
for (AnnotationContent content : annotation.getContents())
if (VARIABLE_KEY.equals(content.getKey())) {
String variableName = parseVariableName(content
.getValue());
if (variableName != null && !"".equals(variableName)) {
Object value = parseVariable(variableName,
content.getValue());
if (value != null)
variables.put(variableName,
value != NULL_VALUE ? value : null);
}
}
return variables;
}
protected String parseVariableName(String str) {
if (str == null || "".equals(str)) //$NON-NLS-1$
return null;
int idx = str.indexOf('=');
if (idx != -1)
return str.substring(0, idx);
return null;
}
private static final Object NULL_VALUE = new Object();
protected Object parseVariable(String variableName, String str) {
if (str.length() > variableName.length() + 1) {
String value = str.substring(variableName.length() + 1);
// is it null ?
if ("null".equals(value))
return NULL_VALUE;
// is it a String variable ?
if (value.length() > 2 && value.charAt(0) == '\''
&& value.charAt(value.length() - 1) == '\'')
return value.substring(1, value.length() - 1);
// is it a Integer variable ?
try {
return new Integer(value);
} catch (NumberFormatException e) {
// NOTHING TO DO HERE
}
}
return null;
}
}