com.vaadin.server.UIProvider Maven / Gradle / Ivy
/*
* Vaadin Framework 7
*
* Copyright (C) 2000-2024 Vaadin Ltd
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See for the full
* license.
*/
package com.vaadin.server;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.vaadin.annotations.PreserveOnRefresh;
import com.vaadin.annotations.Push;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.Title;
import com.vaadin.annotations.Widgetset;
import com.vaadin.shared.communication.PushMode;
import com.vaadin.shared.ui.ui.Transport;
import com.vaadin.ui.UI;
public abstract class UIProvider implements Serializable {
/* Default widgetset name to look for */
private static final String APP_WIDGETSET_NAME = "AppWidgetset";
public abstract Class extends UI> getUIClass(UIClassSelectionEvent event);
public UI createInstance(UICreateEvent event) {
try {
return event.getUIClass().newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Could not instantiate UI class", e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Could not access UI class", e);
}
}
/**
* Helper to get an annotation for a class. If the annotation is not present
* on the target class, its super classes and directly implemented
* interfaces are also searched for the annotation. Interfaces implemented
* by superclasses are not taken into account.
*
* Note that searching implemented interfaces for {@code @Inherited}
* annotations and searching for superclasses for non-inherited annotations
* do not follow the standard semantics and are supported for backwards
* compatibility. Future versions of the framework might only support the
* standard semantics of {@code @Inherited}.
*
* @param clazz
* the class from which the annotation should be found
* @param annotationType
* the annotation type to look for
* @return an annotation of the given type, or null
if the
* annotation is not present on the class
*/
protected static T getAnnotationFor(Class> clazz,
Class annotationType) {
// Don't discover hierarchy if annotation is inherited
if (annotationType.getAnnotation(Inherited.class) != null) {
T annotation = clazz.getAnnotation(annotationType);
if (annotation != null) {
return annotation;
}
} else {
// Find from the class hierarchy
Class> currentType = clazz;
while (currentType != Object.class) {
T annotation = currentType.getAnnotation(annotationType);
if (annotation != null) {
return annotation;
} else {
currentType = currentType.getSuperclass();
}
}
}
// Find from a directly implemented interface
for (Class> iface : clazz.getInterfaces()) {
T annotation = iface.getAnnotation(annotationType);
if (annotation != null) {
return annotation;
}
}
return null;
}
/**
* Finds the theme to use for a specific UI. If no specific theme is
* required, null
is returned.
*
* The default implementation checks for a @{@link Theme} annotation on the
* UI class.
*
* @param event
* the UI create event with information about the UI and the
* current request.
* @return the name of the theme, or null
if the default theme
* should be used
*
*/
public String getTheme(UICreateEvent event) {
Theme uiTheme = getAnnotationFor(event.getUIClass(), Theme.class);
if (uiTheme != null) {
return uiTheme.value();
} else {
return null;
}
}
/**
* Finds the widgetset to use for a specific UI. If no specific widgetset is
* required, null
is returned.
*
* This method uses the Widgetset definition priority order from
* {@link #getWidgetsetInfo(UICreateEvent)}.
*
* Note: This method exists only for backwards
* compatibility and overriding it won't have the effect it used to.
*
* @param event
* the UI create event with information about the UI and the
* current request.
* @return the name of the widgetset, or null
if the default
* widgetset should be used
* @deprecated This method has been replaced by
* {@link #getWidgetsetInfo(UICreateEvent)} in 7.7
*/
@Deprecated
public String getWidgetset(UICreateEvent event) {
WidgetsetInfo widgetsetInfo = getWidgetsetInfo(event);
return widgetsetInfo != null ? widgetsetInfo.getWidgetsetName() : null;
}
/**
* Finds the widgetset to use for a specific UI. If no specific widgetset is
* required, null
is returned.
*
* The default implementation uses the following order of priority for
* finding the widgetset information:
*
* - @{@link Widgetset} annotation if it is defined for the UI class
* - The class AppWidgetset if one exists in the default package
* - A widgetset called AppWidgetset if there is an AppWidgetset.gwt.xml
* file in the default package
* - null to use the default widgetset otherwise
*
*
* @since 7.7
*
* @param event
* the UI create event with information about the UI and the
* current request.
* @return the widgetset info, or null
if the default widgetset
* should be used
*/
public WidgetsetInfo getWidgetsetInfo(UICreateEvent event) {
Widgetset uiWidgetset = getAnnotationFor(event.getUIClass(),
Widgetset.class);
// First case: We have an @Widgetset annotation, use that
if (uiWidgetset != null) {
return new WidgetsetInfoImpl(uiWidgetset.value());
}
// Second case: We might have an init parameter, use that
String initParameterWidgetSet = event.getService()
.getDeploymentConfiguration().getWidgetset(null);
if (initParameterWidgetSet != null) {
return new WidgetsetInfoImpl(initParameterWidgetSet);
}
// Find the class AppWidgetset in the default package if one exists
WidgetsetInfo info = getWidgetsetClassInfo();
// Third case: we have a generated class called APP_WIDGETSET_NAME
if (info != null) {
return info;
}
// Fourth case: we have an AppWidgetset.gwt.xml file
else {
InputStream resource = event.getUIClass()
.getResourceAsStream("/" + APP_WIDGETSET_NAME + ".gwt.xml");
if (resource != null) {
return new WidgetsetInfoImpl(false, null, APP_WIDGETSET_NAME);
}
}
// fourth case: we are using the default widgetset
return null;
}
private Class findWidgetsetClass() {
try {
// We cannot naively use Class.forname without getting the correct
// classloader
// FIXME This might still fail with osgi
ClassLoader tccl = VaadinService.getCurrent().getClassLoader();
Class> c = Class.forName(APP_WIDGETSET_NAME, true, tccl);
// if not implementing the interface, possibly a @WebListener class
// from an earlier version - ignore it
if (WidgetsetInfo.class.isAssignableFrom(c)) {
return (Class) c;
}
} catch (ClassNotFoundException e) {
// ClassNotFound is a normal case
}
return null;
}
private WidgetsetInfo getWidgetsetClassInfo() {
Class cls = findWidgetsetClass();
if (cls != null) {
try {
return cls.newInstance();
} catch (InstantiationException e) {
getLogger().log(Level.INFO,
"Unexpected trying to instantiate class "
+ cls.getName(),
e);
} catch (IllegalAccessException e) {
getLogger().log(Level.INFO,
"Unexpected trying to access class " + cls.getName(),
e);
}
}
return null;
}
/**
* Checks whether the same UI state should be reused if the framework can
* detect that the application is opened in a browser window where it has
* previously been open. The framework attempts to discover this by checking
* the value of window.name in the browser.
*
* Whenever a preserved UI is reused, its
* {@link UI#refresh(com.vaadin.server.VaadinRequest) refresh} method is
* invoked by the framework first.
*
*
* @param event
* the UI create event with information about the UI and the
* current request.
*
* @return true
if the same UI instance should be reused e.g.
* when the browser window is refreshed.
*/
public boolean isPreservedOnRefresh(UICreateEvent event) {
PreserveOnRefresh preserveOnRefresh = getAnnotationFor(
event.getUIClass(), PreserveOnRefresh.class);
return preserveOnRefresh != null;
}
public String getPageTitle(UICreateEvent event) {
Title titleAnnotation = getAnnotationFor(event.getUIClass(),
Title.class);
if (titleAnnotation == null) {
return null;
} else {
return titleAnnotation.value();
}
}
/**
* Finds the {@link PushMode} to use for a specific UI. If no specific push
* mode is required, null
is returned.
*
* The default implementation uses the @{@link Push} annotation if it's
* defined for the UI class.
*
* @param event
* the UI create event with information about the UI and the
* current request.
* @return the push mode to use, or null
if the default push
* mode should be used
*
*/
public PushMode getPushMode(UICreateEvent event) {
Push push = getAnnotationFor(event.getUIClass(), Push.class);
if (push == null) {
return null;
} else {
return push.value();
}
}
/**
* Finds the {@link Transport} to use for a specific UI. If no transport is
* defined, null
is returned.
*
* The default implementation uses the @{@link Push} annotation if it's
* defined for the UI class.
*
* @param event
* the UI create event with information about the UI and the
* current request.
* @return the transport type to use, or null
if the default
* transport type should be used
*/
public Transport getPushTransport(UICreateEvent event) {
Push push = getAnnotationFor(event.getUIClass(), Push.class);
if (push == null) {
return null;
} else {
return push.transport();
}
}
private static final Logger getLogger() {
return Logger.getLogger(UIProvider.class.getName());
}
}