/* * Copyright (C) 2011 Rhegium Team * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.rhegium.api.uibinder; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.nio.charset.Charset; import java.util.Locale; import java.util.Map; import javax.xml.parsers.SAXParserFactory; import org.rhegium.api.i18n.LanguageService; import org.rhegium.api.mvc.AbstractBindableView; import org.rhegium.api.mvc.Controller; import org.rhegium.api.mvc.View; import org.rhegium.api.typeconverter.TypeConverterManager; import org.rhegium.internal.utils.StringUtils; import com.google.inject.Inject; public abstract class AbstractUiBinderService<C> implements UiBinderService<C> { private final SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); @Inject private LanguageService languageService; @Inject private TypeConverterManager typeConverterManager; @Inject private UiBinderEventService uiBinderEventService; public AbstractUiBinderService() { saxParserFactory.setNamespaceAware(true); saxParserFactory.setValidating(false); } @Override public <CC extends Controller<C, CC, V>, V extends View<C, CC, V>> V bindView(V view, Locale locale) { if (!(view instanceof AbstractBindableView)) { return view; } // Remove all existing components from view C content = prepareView(view); // Get XML definition stream String xmlDefinition = resolveXmlDefinition(view.getClass()); InputStream stream = getResource(xmlDefinition); // Build up the Component tree bind(content, stream, view, view, locale); // Initialize view if (view instanceof InitializableView) { ((InitializableView) view).initializeView(); } // Return filled view return view; } @Override public <CC extends Controller<C, CC, V>, V extends View<C, CC, V>> V bindView(V view, String xml, Locale locale) { if (!(view instanceof AbstractBindableView)) { return view; } // Remove all existing components from view C content = prepareView(view); // Get XML definition stream InputStream stream = new ByteArrayInputStream(xml.getBytes(Charset.forName("UTF-8"))); // Build up the Component tree bind(content, stream, view, view, locale); // Initialize view if (view instanceof InitializableView) { ((InitializableView) view).initializeView(); } // Return filled view return view; } @Override public C bind(Class<? extends C> componentClass, View<C, ?, ?> view, Locale locale) { try { C component = componentClass.newInstance(); String xmlDefinition = resolveXmlDefinition(componentClass); InputStream stream = getResource(xmlDefinition); return bind(component, stream, component, view, locale); } catch (InstantiationException e) { throw new UiBinderException(e); } catch (IllegalAccessException e) { throw new UiBinderException(e); } } @Override public C bind(String componentName, View<C, ?, ?> view, Locale locale) { C component = newBaseComposite(); String xmlDefinition = getViewXmlResource(componentName); InputStream stream = getResource(xmlDefinition); return bind(component, stream, component, view, locale); } @Override public boolean isBindable(String componentName) { Class<?> viewClass = getComponentClass(componentName); if (viewClass != null) { return isBindable(viewClass); } InputStream resource = getResource(getViewXmlResource(componentName)); return resource != null; } @Override public boolean isBindable(Class<?> componentClass) { return UiBindable.class.isAssignableFrom(componentClass); } private String getViewXmlResource(String viewName) { return viewName.replace(".", "/") + ".xml"; } private InputStream getResource(String xmlDefinition) { return getClass().getClassLoader().getResourceAsStream(xmlDefinition); } @SuppressWarnings("unchecked") private Class<? extends C> getComponentClass(String componentName) { try { return (Class<? extends C>) Class.forName(componentName); } catch (ClassNotFoundException e) { } return null; } protected TypeConverterManager getTypeConverterManager() { return typeConverterManager; } protected LanguageService getLanguageService() { return languageService; } protected UiBinderEventService getUiBinderEventService() { return uiBinderEventService; } protected SAXParserFactory getSaxParserFactory() { return saxParserFactory; } protected abstract C bind(C component, InputStream stream, Object injectee, View<C, ?, ?> view, Locale locale); protected abstract C newBaseComposite(); protected abstract C prepareView(View<C, ?, ?> view); private String resolveXmlDefinition(Class<?> clazz) { String canonicalName = clazz.getCanonicalName(); return getViewXmlResource(canonicalName); } protected void injectBindings(Object injectee, Map<String, C> components) { injectBindings(injectee, components, injectee.getClass()); } private void injectBindings(Object injectee, Map<String, C> components, Class<?> injecteeClass) { // Synchronize to force context update after injection to make fields // available to all others threads synchronized (injectee) { for (Field field : injecteeClass.getDeclaredFields()) { if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) { continue; } if (!field.isAnnotationPresent(InjectUi.class)) { continue; } InjectUi injectUi = field.getAnnotation(InjectUi.class); String property = StringUtils.isEmpty(injectUi.value()) ? field.getName() : injectUi.value(); if ("$$abstractBindableViewComponents$$".equals(property)) { setValue(field, injectee, components); } else { C component = components.get(property); if (component == null) { throw new UiBinderException("UiField " + field.getName() + " on type " + injecteeClass.getCanonicalName() + " could not be injected since no component with id " + property + " exists"); } setValue(field, injectee, component); } } if (injecteeClass.getSuperclass() != null && injecteeClass.getSuperclass() != Object.class) { injectBindings(injectee, components, injecteeClass.getSuperclass()); } } } private void setValue(Field field, Object injectee, Object value) { try { field.setAccessible(true); field.set(injectee, value); } catch (IllegalAccessException e) { throw new UiBinderException(e); } } }