Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

AbstractSingleComponentContainer.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  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.ui;
  17. import java.util.Collections;
  18. import java.util.Iterator;
  19. import org.jsoup.nodes.Element;
  20. import org.jsoup.select.Elements;
  21. import com.vaadin.server.ComponentSizeValidator;
  22. import com.vaadin.server.VaadinService;
  23. import com.vaadin.server.VaadinSession;
  24. import com.vaadin.ui.declarative.DesignContext;
  25. import com.vaadin.ui.declarative.DesignException;
  26. /**
  27. * Abstract base class for component containers that have only one child
  28. * component.
  29. *
  30. * For component containers that support multiple children, inherit
  31. * {@link AbstractComponentContainer} instead of this class.
  32. *
  33. * @since 7.0
  34. */
  35. public abstract class AbstractSingleComponentContainer extends
  36. AbstractComponent implements SingleComponentContainer {
  37. private Component content;
  38. @Override
  39. public int getComponentCount() {
  40. return (content != null) ? 1 : 0;
  41. }
  42. @Override
  43. public Iterator<Component> iterator() {
  44. if (content != null) {
  45. return Collections.singletonList(content).iterator();
  46. } else {
  47. return Collections.<Component> emptyList().iterator();
  48. }
  49. }
  50. /* documented in interface */
  51. @Override
  52. public void addComponentAttachListener(ComponentAttachListener listener) {
  53. addListener(ComponentAttachEvent.class, listener,
  54. ComponentAttachListener.attachMethod);
  55. }
  56. /* documented in interface */
  57. @Override
  58. public void removeComponentAttachListener(ComponentAttachListener listener) {
  59. removeListener(ComponentAttachEvent.class, listener,
  60. ComponentAttachListener.attachMethod);
  61. }
  62. /* documented in interface */
  63. @Override
  64. public void addComponentDetachListener(ComponentDetachListener listener) {
  65. addListener(ComponentDetachEvent.class, listener,
  66. ComponentDetachListener.detachMethod);
  67. }
  68. /* documented in interface */
  69. @Override
  70. public void removeComponentDetachListener(ComponentDetachListener listener) {
  71. removeListener(ComponentDetachEvent.class, listener,
  72. ComponentDetachListener.detachMethod);
  73. }
  74. /**
  75. * Fires the component attached event. This is called by the
  76. * {@link #setContent(Component)} method after the component has been set as
  77. * the content.
  78. *
  79. * @param component
  80. * the component that has been added to this container.
  81. */
  82. protected void fireComponentAttachEvent(Component component) {
  83. fireEvent(new ComponentAttachEvent(this, component));
  84. }
  85. /**
  86. * Fires the component detached event. This is called by the
  87. * {@link #setContent(Component)} method after the content component has
  88. * been replaced by other content.
  89. *
  90. * @param component
  91. * the component that has been removed from this container.
  92. */
  93. protected void fireComponentDetachEvent(Component component) {
  94. fireEvent(new ComponentDetachEvent(this, component));
  95. }
  96. @Override
  97. public Component getContent() {
  98. return content;
  99. }
  100. /**
  101. * Sets the content of this container. The content is a component that
  102. * serves as the outermost item of the visual contents.
  103. *
  104. * The content must always be set, either with a constructor parameter or by
  105. * calling this method.
  106. *
  107. * Previous versions of Vaadin used a {@link VerticalLayout} with margins
  108. * enabled as the default content but that is no longer the case.
  109. *
  110. * @param content
  111. * a component (typically a layout) to use as content
  112. */
  113. @Override
  114. public void setContent(Component content) {
  115. // Make sure we're not adding the component inside it's own content
  116. if (isOrHasAncestor(content)) {
  117. throw new IllegalArgumentException(
  118. "Component cannot be added inside it's own content");
  119. }
  120. Component oldContent = getContent();
  121. if (oldContent == content) {
  122. // do not set the same content twice
  123. return;
  124. }
  125. if (oldContent != null && equals(oldContent.getParent())) {
  126. oldContent.setParent(null);
  127. fireComponentDetachEvent(oldContent);
  128. }
  129. this.content = content;
  130. if (content != null) {
  131. removeFromParent(content);
  132. content.setParent(this);
  133. fireComponentAttachEvent(content);
  134. }
  135. markAsDirty();
  136. }
  137. /**
  138. * Utility method for removing a component from its parent (if possible).
  139. *
  140. * @param content
  141. * component to remove
  142. */
  143. // TODO move utility method elsewhere?
  144. public static void removeFromParent(Component content)
  145. throws IllegalArgumentException {
  146. // Verify the appropriate session is locked
  147. UI parentUI = content.getUI();
  148. if (parentUI != null) {
  149. VaadinSession parentSession = parentUI.getSession();
  150. if (parentSession != null && !parentSession.hasLock()) {
  151. String message = "Cannot remove from parent when the session is not locked.";
  152. if (VaadinService.isOtherSessionLocked(parentSession)) {
  153. message += " Furthermore, there is another locked session, indicating that the component might be about to be moved from one session to another.";
  154. }
  155. throw new IllegalStateException(message);
  156. }
  157. }
  158. HasComponents parent = content.getParent();
  159. if (parent instanceof ComponentContainer) {
  160. // If the component already has a parent, try to remove it
  161. ComponentContainer oldParent = (ComponentContainer) parent;
  162. oldParent.removeComponent(content);
  163. } else if (parent instanceof SingleComponentContainer) {
  164. SingleComponentContainer oldParent = (SingleComponentContainer) parent;
  165. if (oldParent.getContent() == content) {
  166. oldParent.setContent(null);
  167. }
  168. } else if (parent != null) {
  169. throw new IllegalArgumentException(
  170. "Content is already attached to another parent");
  171. }
  172. }
  173. // the setHeight()/setWidth() methods duplicated and simplified from
  174. // AbstractComponentContainer
  175. @Override
  176. public void setWidth(float width, Unit unit) {
  177. /*
  178. * child tree repaints may be needed, due to our fall back support for
  179. * invalid relative sizes
  180. */
  181. boolean dirtyChild = false;
  182. boolean childrenMayBecomeUndefined = false;
  183. if (getWidth() == SIZE_UNDEFINED && width != SIZE_UNDEFINED) {
  184. // children currently in invalid state may need repaint
  185. dirtyChild = getInvalidSizedChild(false);
  186. } else if ((width == SIZE_UNDEFINED && getWidth() != SIZE_UNDEFINED)
  187. || (unit == Unit.PERCENTAGE
  188. && getWidthUnits() != Unit.PERCENTAGE && !ComponentSizeValidator
  189. .parentCanDefineWidth(this))) {
  190. /*
  191. * relative width children may get to invalid state if width becomes
  192. * invalid. Width may also become invalid if units become percentage
  193. * due to the fallback support
  194. */
  195. childrenMayBecomeUndefined = true;
  196. dirtyChild = getInvalidSizedChild(false);
  197. }
  198. super.setWidth(width, unit);
  199. repaintChangedChildTree(dirtyChild, childrenMayBecomeUndefined, false);
  200. }
  201. private void repaintChangedChildTree(boolean invalidChild,
  202. boolean childrenMayBecomeUndefined, boolean vertical) {
  203. if (getContent() == null) {
  204. return;
  205. }
  206. boolean needRepaint = false;
  207. if (childrenMayBecomeUndefined) {
  208. // if became invalid now
  209. needRepaint = !invalidChild && getInvalidSizedChild(vertical);
  210. } else if (invalidChild) {
  211. // if not still invalid
  212. needRepaint = !getInvalidSizedChild(vertical);
  213. }
  214. if (needRepaint) {
  215. getContent().markAsDirtyRecursive();
  216. }
  217. }
  218. private boolean getInvalidSizedChild(final boolean vertical) {
  219. Component content = getContent();
  220. if (content == null) {
  221. return false;
  222. }
  223. if (vertical) {
  224. return !ComponentSizeValidator.checkHeights(content);
  225. } else {
  226. return !ComponentSizeValidator.checkWidths(content);
  227. }
  228. }
  229. @Override
  230. public void setHeight(float height, Unit unit) {
  231. /*
  232. * child tree repaints may be needed, due to our fall back support for
  233. * invalid relative sizes
  234. */
  235. boolean dirtyChild = false;
  236. boolean childrenMayBecomeUndefined = false;
  237. if (getHeight() == SIZE_UNDEFINED && height != SIZE_UNDEFINED) {
  238. // children currently in invalid state may need repaint
  239. dirtyChild = getInvalidSizedChild(true);
  240. } else if ((height == SIZE_UNDEFINED && getHeight() != SIZE_UNDEFINED)
  241. || (unit == Unit.PERCENTAGE
  242. && getHeightUnits() != Unit.PERCENTAGE && !ComponentSizeValidator
  243. .parentCanDefineHeight(this))) {
  244. /*
  245. * relative height children may get to invalid state if height
  246. * becomes invalid. Height may also become invalid if units become
  247. * percentage due to the fallback support.
  248. */
  249. childrenMayBecomeUndefined = true;
  250. dirtyChild = getInvalidSizedChild(true);
  251. }
  252. super.setHeight(height, unit);
  253. repaintChangedChildTree(dirtyChild, childrenMayBecomeUndefined, true);
  254. }
  255. /*
  256. * (non-Javadoc)
  257. *
  258. * @see com.vaadin.ui.AbstractComponent#readDesign(org.jsoup.nodes .Element,
  259. * com.vaadin.ui.declarative.DesignContext)
  260. */
  261. @Override
  262. public void readDesign(Element design, DesignContext designContext) {
  263. // process default attributes
  264. super.readDesign(design, designContext);
  265. readDesignChildren(design.children(), designContext);
  266. }
  267. /**
  268. * Reads the content component from the list of child elements of a design.
  269. * The list must be empty or contain a single element; if the design
  270. * contains multiple child elements, a DesignException is thrown. This
  271. * method should be overridden by subclasses whose design may contain
  272. * non-content child elements.
  273. *
  274. * @since 7.5.0
  275. *
  276. * @param children
  277. * the child elements of the design that is being read
  278. * @param context
  279. * the DesignContext instance used to parse the design
  280. *
  281. * @throws DesignException
  282. * if there are multiple child elements
  283. * @throws DesignException
  284. * if a child element could not be parsed as a Component
  285. */
  286. protected void readDesignChildren(Elements children, DesignContext context) {
  287. if (children.size() > 1) {
  288. throw new DesignException("The container of type "
  289. + getClass().toString()
  290. + " can have only one child component.");
  291. } else if (children.size() == 1) {
  292. setContent(context.readDesign(children.first()));
  293. }
  294. }
  295. /*
  296. * (non-Javadoc)
  297. *
  298. * @see com.vaadin.ui.AbstractComponent#writeDesign(org.jsoup.nodes.Element
  299. * , com.vaadin.ui.declarative.DesignContext)
  300. */
  301. @Override
  302. public void writeDesign(Element design, DesignContext designContext) {
  303. // write default attributes (also clears children and attributes)
  304. super.writeDesign(design, designContext);
  305. AbstractSingleComponentContainer def = designContext
  306. .getDefaultInstance(this);
  307. if (!designContext.shouldWriteChildren(this, def)) {
  308. return;
  309. }
  310. // handle child component
  311. Component child = getContent();
  312. if (child != null) {
  313. Element childNode = designContext.createElement(child);
  314. design.appendChild(childNode);
  315. }
  316. }
  317. }