Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. /*
  2. * Copyright 2000-2014 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.server;
  17. import java.io.InputStream;
  18. import java.io.Serializable;
  19. import java.lang.annotation.Annotation;
  20. import java.lang.annotation.Inherited;
  21. import java.util.logging.Level;
  22. import java.util.logging.Logger;
  23. import com.vaadin.annotations.PreserveOnRefresh;
  24. import com.vaadin.annotations.Push;
  25. import com.vaadin.annotations.Theme;
  26. import com.vaadin.annotations.Title;
  27. import com.vaadin.annotations.Widgetset;
  28. import com.vaadin.shared.communication.PushMode;
  29. import com.vaadin.shared.ui.ui.Transport;
  30. import com.vaadin.ui.UI;
  31. public abstract class UIProvider implements Serializable {
  32. /* Default widgetset name to look for */
  33. private static final String APP_WIDGETSET_NAME = "AppWidgetset";
  34. public abstract Class<? extends UI> getUIClass(UIClassSelectionEvent event);
  35. public UI createInstance(UICreateEvent event) {
  36. try {
  37. return event.getUIClass().newInstance();
  38. } catch (InstantiationException e) {
  39. throw new RuntimeException("Could not instantiate UI class", e);
  40. } catch (IllegalAccessException e) {
  41. throw new RuntimeException("Could not access UI class", e);
  42. }
  43. }
  44. /**
  45. * Helper to get an annotation for a class. If the annotation is not present
  46. * on the target class, its super classes and directly implemented
  47. * interfaces are also searched for the annotation. Interfaces implemented
  48. * by superclasses are not taken into account.
  49. * <p>
  50. * Note that searching implemented interfaces for {@code @Inherited}
  51. * annotations and searching for superclasses for non-inherited annotations
  52. * do not follow the standard semantics and are supported for backwards
  53. * compatibility. Future versions of the framework might only support the
  54. * standard semantics of {@code @Inherited}.
  55. *
  56. * @param clazz
  57. * the class from which the annotation should be found
  58. * @param annotationType
  59. * the annotation type to look for
  60. * @return an annotation of the given type, or <code>null</code> if the
  61. * annotation is not present on the class
  62. */
  63. protected static <T extends Annotation> T getAnnotationFor(Class<?> clazz,
  64. Class<T> annotationType) {
  65. // Don't discover hierarchy if annotation is inherited
  66. if (annotationType.getAnnotation(Inherited.class) != null) {
  67. T annotation = clazz.getAnnotation(annotationType);
  68. if (annotation != null) {
  69. return annotation;
  70. }
  71. } else {
  72. // Find from the class hierarchy
  73. Class<?> currentType = clazz;
  74. while (currentType != Object.class) {
  75. T annotation = currentType.getAnnotation(annotationType);
  76. if (annotation != null) {
  77. return annotation;
  78. } else {
  79. currentType = currentType.getSuperclass();
  80. }
  81. }
  82. }
  83. // Find from a directly implemented interface
  84. for (Class<?> iface : clazz.getInterfaces()) {
  85. T annotation = iface.getAnnotation(annotationType);
  86. if (annotation != null) {
  87. return annotation;
  88. }
  89. }
  90. return null;
  91. }
  92. /**
  93. * Finds the theme to use for a specific UI. If no specific theme is
  94. * required, <code>null</code> is returned.
  95. * <p>
  96. * The default implementation checks for a @{@link Theme} annotation on the
  97. * UI class.
  98. *
  99. * @param event
  100. * the UI create event with information about the UI and the
  101. * current request.
  102. * @return the name of the theme, or <code>null</code> if the default theme
  103. * should be used
  104. *
  105. */
  106. public String getTheme(UICreateEvent event) {
  107. Theme uiTheme = getAnnotationFor(event.getUIClass(), Theme.class);
  108. if (uiTheme != null) {
  109. return uiTheme.value();
  110. } else {
  111. return null;
  112. }
  113. }
  114. /**
  115. * Finds the widgetset to use for a specific UI. If no specific widgetset is
  116. * required, <code>null</code> is returned.
  117. * <p>
  118. * This method uses the Widgetset definition priority order from
  119. * {@link #getWidgetsetInfo(UICreateEvent)}.
  120. * <p>
  121. * <strong>Note:</strong> This method exists only for backwards
  122. * compatibility and overriding it won't have the effect it used to.
  123. *
  124. * @param event
  125. * the UI create event with information about the UI and the
  126. * current request.
  127. * @return the name of the widgetset, or <code>null</code> if the default
  128. * widgetset should be used
  129. * @deprecated This method has been replaced by
  130. * {@link #getWidgetsetInfo(UICreateEvent)} in 7.7
  131. */
  132. @Deprecated
  133. public String getWidgetset(UICreateEvent event) {
  134. WidgetsetInfo widgetsetInfo = getWidgetsetInfo(event);
  135. return widgetsetInfo != null ? widgetsetInfo.getWidgetsetName() : null;
  136. }
  137. /**
  138. * Finds the widgetset to use for a specific UI. If no specific widgetset is
  139. * required, <code>null</code> is returned.
  140. * <p>
  141. * The default implementation uses the following order of priority for
  142. * finding the widgetset information:
  143. * <ul>
  144. * <li>@{@link Widgetset} annotation if it is defined for the UI class</li>
  145. * <li>The class AppWidgetset if one exists in the default package</li>
  146. * <li>A widgetset called AppWidgetset if there is an AppWidgetset.gwt.xml
  147. * file in the default package</li>
  148. * <li>null to use the default widgetset otherwise</li>
  149. * </ul>
  150. *
  151. * @since 7.7
  152. *
  153. * @param event
  154. * the UI create event with information about the UI and the
  155. * current request.
  156. * @return the widgetset info, or <code>null</code> if the default widgetset
  157. * should be used
  158. */
  159. public WidgetsetInfo getWidgetsetInfo(UICreateEvent event) {
  160. Widgetset uiWidgetset = getAnnotationFor(event.getUIClass(),
  161. Widgetset.class);
  162. // First case: We have an @Widgetset annotation, use that
  163. if (uiWidgetset != null) {
  164. return new WidgetsetInfoImpl(uiWidgetset.value());
  165. }
  166. // Second case: We might have an init parameter, use that
  167. String initParameterWidgetSet = event.getService()
  168. .getDeploymentConfiguration().getWidgetset(null);
  169. if (initParameterWidgetSet != null) {
  170. return new WidgetsetInfoImpl(initParameterWidgetSet);
  171. }
  172. // Find the class AppWidgetset in the default package if one exists
  173. WidgetsetInfo info = getWidgetsetClassInfo();
  174. // Third case: we have a generated class called APP_WIDGETSET_NAME
  175. if (info != null) {
  176. return info;
  177. }
  178. // Fourth case: we have an AppWidgetset.gwt.xml file
  179. else {
  180. InputStream resource = event.getUIClass()
  181. .getResourceAsStream("/" + APP_WIDGETSET_NAME + ".gwt.xml");
  182. if (resource != null) {
  183. return new WidgetsetInfoImpl(false, null, APP_WIDGETSET_NAME);
  184. }
  185. }
  186. // fourth case: we are using the default widgetset
  187. return null;
  188. }
  189. private Class<WidgetsetInfo> findWidgetsetClass() {
  190. try {
  191. // We cannot naively use Class.forname without getting the correct
  192. // classloader
  193. // FIXME This might still fail with osgi
  194. ClassLoader tccl = VaadinService.getCurrent().getClassLoader();
  195. Class<?> c = Class.forName(APP_WIDGETSET_NAME, true, tccl);
  196. // if not implementing the interface, possibly a @WebListener class
  197. // from an earlier version - ignore it
  198. if (WidgetsetInfo.class.isAssignableFrom(c)) {
  199. return (Class<WidgetsetInfo>) c;
  200. }
  201. } catch (ClassNotFoundException e) {
  202. // ClassNotFound is a normal case
  203. }
  204. return null;
  205. }
  206. private WidgetsetInfo getWidgetsetClassInfo() {
  207. Class<WidgetsetInfo> cls = findWidgetsetClass();
  208. if (cls != null) {
  209. try {
  210. return cls.newInstance();
  211. } catch (InstantiationException e) {
  212. getLogger().log(Level.INFO,
  213. "Unexpected trying to instantiate class "
  214. + cls.getName(),
  215. e);
  216. } catch (IllegalAccessException e) {
  217. getLogger().log(Level.INFO,
  218. "Unexpected trying to access class " + cls.getName(),
  219. e);
  220. }
  221. }
  222. return null;
  223. }
  224. /**
  225. * Checks whether the same UI state should be reused if the framework can
  226. * detect that the application is opened in a browser window where it has
  227. * previously been open. The framework attempts to discover this by checking
  228. * the value of window.name in the browser.
  229. * <p>
  230. * Whenever a preserved UI is reused, its
  231. * {@link UI#refresh(com.vaadin.server.VaadinRequest) refresh} method is
  232. * invoked by the framework first.
  233. *
  234. *
  235. * @param event
  236. * the UI create event with information about the UI and the
  237. * current request.
  238. *
  239. * @return <code>true</code>if the same UI instance should be reused e.g.
  240. * when the browser window is refreshed.
  241. */
  242. public boolean isPreservedOnRefresh(UICreateEvent event) {
  243. PreserveOnRefresh preserveOnRefresh = getAnnotationFor(
  244. event.getUIClass(), PreserveOnRefresh.class);
  245. return preserveOnRefresh != null;
  246. }
  247. public String getPageTitle(UICreateEvent event) {
  248. Title titleAnnotation = getAnnotationFor(event.getUIClass(),
  249. Title.class);
  250. if (titleAnnotation == null) {
  251. return null;
  252. } else {
  253. return titleAnnotation.value();
  254. }
  255. }
  256. /**
  257. * Finds the {@link PushMode} to use for a specific UI. If no specific push
  258. * mode is required, <code>null</code> is returned.
  259. * <p>
  260. * The default implementation uses the @{@link Push} annotation if it's
  261. * defined for the UI class.
  262. *
  263. * @param event
  264. * the UI create event with information about the UI and the
  265. * current request.
  266. * @return the push mode to use, or <code>null</code> if the default push
  267. * mode should be used
  268. *
  269. */
  270. public PushMode getPushMode(UICreateEvent event) {
  271. Push push = getAnnotationFor(event.getUIClass(), Push.class);
  272. if (push == null) {
  273. return null;
  274. } else {
  275. return push.value();
  276. }
  277. }
  278. /**
  279. * Finds the {@link Transport} to use for a specific UI. If no transport is
  280. * defined, <code>null</code> is returned.
  281. * <p>
  282. * The default implementation uses the @{@link Push} annotation if it's
  283. * defined for the UI class.
  284. *
  285. * @param event
  286. * the UI create event with information about the UI and the
  287. * current request.
  288. * @return the transport type to use, or <code>null</code> if the default
  289. * transport type should be used
  290. */
  291. public Transport getPushTransport(UICreateEvent event) {
  292. Push push = getAnnotationFor(event.getUIClass(), Push.class);
  293. if (push == null) {
  294. return null;
  295. } else {
  296. return push.transport();
  297. }
  298. }
  299. private static final Logger getLogger() {
  300. return Logger.getLogger(UIProvider.class.getName());
  301. }
  302. }