You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DesignContext.java 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  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.declarative;
  17. import java.io.Serializable;
  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. import java.util.Collections;
  21. import java.util.HashMap;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.concurrent.ConcurrentHashMap;
  25. import org.jsoup.nodes.Attributes;
  26. import org.jsoup.nodes.Document;
  27. import org.jsoup.nodes.Element;
  28. import org.jsoup.nodes.Node;
  29. import com.vaadin.annotations.DesignRoot;
  30. import com.vaadin.ui.Component;
  31. import com.vaadin.ui.HasComponents;
  32. import com.vaadin.ui.declarative.Design.ComponentFactory;
  33. import com.vaadin.ui.declarative.Design.ComponentMapper;
  34. /**
  35. * This class contains contextual information that is collected when a component
  36. * tree is constructed based on HTML design template. This information includes
  37. * mappings from local ids, global ids and captions to components , as well as a
  38. * mapping between prefixes and package names (such as "v" -> "com.vaadin.ui").
  39. *
  40. * @since 7.4
  41. * @author Vaadin Ltd
  42. */
  43. public class DesignContext implements Serializable {
  44. // cache for object instances
  45. private static Map<Class<?>, Component> instanceCache = new ConcurrentHashMap<Class<?>, Component>();
  46. // The root component of the component hierarchy
  47. private Component rootComponent = null;
  48. // Attribute names for global id and caption and the prefix name for a local
  49. // id
  50. public static final String ID_ATTRIBUTE = "id";
  51. public static final String CAPTION_ATTRIBUTE = "caption";
  52. public static final String LOCAL_ID_ATTRIBUTE = "_id";
  53. // Mappings from ids to components. Modified when reading from design.
  54. private Map<String, Component> idToComponent = new HashMap<String, Component>();
  55. private Map<String, Component> localIdToComponent = new HashMap<String, Component>();
  56. private Map<String, Component> captionToComponent = new HashMap<String, Component>();
  57. // Mapping from components to local ids. Accessed when writing to
  58. // design. Modified when reading from design.
  59. private Map<Component, String> componentToLocalId = new HashMap<Component, String>();
  60. private Document doc; // required for calling createElement(String)
  61. // namespace mappings
  62. private Map<String, String> packageToPrefix = new HashMap<String, String>();
  63. private Map<String, String> prefixToPackage = new HashMap<String, String>();
  64. // prefix names for which no package-mapping element will be created in the
  65. // html tree (this includes at least "v" which is always taken to refer
  66. // to "com.vaadin.ui".
  67. private Map<String, String> defaultPrefixes = new HashMap<String, String>();
  68. // component creation listeners
  69. private List<ComponentCreationListener> listeners = new ArrayList<ComponentCreationListener>();
  70. private ShouldWriteDataDelegate shouldWriteDataDelegate = ShouldWriteDataDelegate.DEFAULT;
  71. public DesignContext(Document doc) {
  72. this.doc = doc;
  73. // Initialize the mapping between prefixes and package names.
  74. defaultPrefixes.put("v", "com.vaadin.ui");
  75. for (String prefix : defaultPrefixes.keySet()) {
  76. String packageName = defaultPrefixes.get(prefix);
  77. addPackagePrefix(prefix, packageName);
  78. }
  79. }
  80. public DesignContext() {
  81. this(new Document(""));
  82. }
  83. /**
  84. * Returns a component having the specified local id. If no component is
  85. * found, returns null.
  86. *
  87. * @param localId
  88. * The local id of the component
  89. * @return a component whose local id equals localId
  90. */
  91. public Component getComponentByLocalId(String localId) {
  92. return localIdToComponent.get(localId);
  93. }
  94. /**
  95. * Returns a component having the specified global id. If no component is
  96. * found, returns null.
  97. *
  98. * @param globalId
  99. * The global id of the component
  100. * @return a component whose global id equals globalId
  101. */
  102. public Component getComponentById(String globalId) {
  103. return idToComponent.get(globalId);
  104. }
  105. /**
  106. * Returns a component having the specified caption. If no component is
  107. * found, returns null.
  108. *
  109. * @param caption
  110. * The caption of the component
  111. * @return a component whose caption equals the caption given as a parameter
  112. */
  113. public Component getComponentByCaption(String caption) {
  114. return captionToComponent.get(caption);
  115. }
  116. /**
  117. * Creates a mapping between the given global id and the component. Returns
  118. * true if globalId was already mapped to some component. Otherwise returns
  119. * false. Also sets the id of the component to globalId.
  120. *
  121. * If there is a mapping from the component to a global id (gid) different
  122. * from globalId, the mapping from gid to component is removed.
  123. *
  124. * If the string was mapped to a component c different from the given
  125. * component, the mapping from c to the string is removed. Similarly, if
  126. * component was mapped to some string s different from globalId, the
  127. * mapping from s to component is removed.
  128. *
  129. * @param globalId
  130. * The new global id of the component.
  131. * @param component
  132. * The component whose global id is to be set.
  133. * @return true, if there already was a global id mapping from the string to
  134. * some component.
  135. */
  136. private boolean mapId(String globalId, Component component) {
  137. Component oldComponent = idToComponent.get(globalId);
  138. if (oldComponent != null && !oldComponent.equals(component)) {
  139. oldComponent.setId(null);
  140. }
  141. String oldGID = component.getId();
  142. if (oldGID != null && !oldGID.equals(globalId)) {
  143. idToComponent.remove(oldGID);
  144. }
  145. component.setId(globalId);
  146. idToComponent.put(globalId, component);
  147. return oldComponent != null && !oldComponent.equals(component);
  148. }
  149. /**
  150. * Creates a mapping between the given local id and the component. Returns
  151. * true if localId was already mapped to some component or if component was
  152. * mapped to some string. Otherwise returns false.
  153. *
  154. * If the string was mapped to a component c different from the given
  155. * component, the mapping from c to the string is removed. Similarly, if
  156. * component was mapped to some string s different from localId, the mapping
  157. * from s to component is removed.
  158. *
  159. * @since 7.5.0
  160. *
  161. * @param component
  162. * The component whose local id is to be set.
  163. * @param localId
  164. * The new local id of the component.
  165. *
  166. * @return true, if there already was a local id mapping from the string to
  167. * some component or from the component to some string. Otherwise
  168. * returns false.
  169. */
  170. public boolean setComponentLocalId(Component component, String localId) {
  171. return twoWayMap(localId, component, localIdToComponent,
  172. componentToLocalId);
  173. }
  174. /**
  175. * Returns the local id for a component.
  176. *
  177. * @since 7.5.0
  178. *
  179. * @param component
  180. * The component whose local id to get.
  181. * @return the local id of the component, or null if the component has no
  182. * local id assigned
  183. */
  184. public String getComponentLocalId(Component component) {
  185. return componentToLocalId.get(component);
  186. }
  187. /**
  188. * Creates a mapping between the given caption and the component. Returns
  189. * true if caption was already mapped to some component.
  190. *
  191. * Note that unlike mapGlobalId, if some component already has the given
  192. * caption, the caption is not cleared from the component. This allows
  193. * non-unique captions. However, only one of the components corresponding to
  194. * a given caption can be found using the map captionToComponent. Hence, any
  195. * captions that are used to identify an object should be unique.
  196. *
  197. * @param caption
  198. * The new caption of the component.
  199. * @param component
  200. * The component whose caption is to be set.
  201. * @return true, if there already was a caption mapping from the string to
  202. * some component.
  203. */
  204. private boolean mapCaption(String caption, Component component) {
  205. return captionToComponent.put(caption, component) != null;
  206. }
  207. /**
  208. * Creates a two-way mapping between key and value, i.e. adds key -> value
  209. * to keyToValue and value -> key to valueToKey. If key was mapped to a
  210. * value v different from the given value, the mapping from v to key is
  211. * removed. Similarly, if value was mapped to some key k different from key,
  212. * the mapping from k to value is removed.
  213. *
  214. * Returns true if there already was a mapping from key to some value v or
  215. * if there was a mapping from value to some key k. Otherwise returns false.
  216. *
  217. * @param key
  218. * The new key in keyToValue.
  219. * @param value
  220. * The new value in keyToValue.
  221. * @param keyToValue
  222. * A map from keys to values.
  223. * @param valueToKey
  224. * A map from values to keys.
  225. * @return whether there already was some mapping from key to a value or
  226. * from value to a key.
  227. */
  228. private <S, T> boolean twoWayMap(S key, T value, Map<S, T> keyToValue,
  229. Map<T, S> valueToKey) {
  230. T oldValue = keyToValue.put(key, value);
  231. if (oldValue != null && !oldValue.equals(value)) {
  232. valueToKey.remove(oldValue);
  233. }
  234. S oldKey = valueToKey.put(value, key);
  235. if (oldKey != null && !oldKey.equals(key)) {
  236. keyToValue.remove(oldKey);
  237. }
  238. return oldValue != null || oldKey != null;
  239. }
  240. /**
  241. * Creates a two-way mapping between a prefix and a package name.
  242. *
  243. * @param prefix
  244. * the prefix name without an ending dash (for instance, "v" is
  245. * by default used for "com.vaadin.ui")
  246. * @param packageName
  247. * the name of the package corresponding to prefix
  248. *
  249. * @see #getPackagePrefixes()
  250. * @see #getPackagePrefix(String)
  251. * @see #getPackage(String)
  252. * @since 7.5.0
  253. */
  254. public void addPackagePrefix(String prefix, String packageName) {
  255. twoWayMap(prefix, packageName, prefixToPackage, packageToPrefix);
  256. }
  257. /**
  258. * Gets the prefix mapping for a given package, or <code>null</code> if
  259. * there is no mapping for the package.
  260. *
  261. * @see #addPackagePrefix(String, String)
  262. * @see #getPackagePrefixes()
  263. *
  264. * @since 7.5.0
  265. * @param packageName
  266. * the package name to get a prefix for
  267. * @return the prefix for the package, or <code>null</code> if no prefix is
  268. * registered
  269. */
  270. public String getPackagePrefix(String packageName) {
  271. return packageToPrefix.get(packageName);
  272. }
  273. /**
  274. * Gets all registered package prefixes.
  275. *
  276. *
  277. * @since 7.5.0
  278. * @see #getPackage(String)
  279. * @return a collection of package prefixes
  280. */
  281. public Collection<String> getPackagePrefixes() {
  282. return Collections.unmodifiableCollection(prefixToPackage.keySet());
  283. }
  284. /**
  285. * Gets the package corresponding to the give prefix, or <code>null</code>
  286. * no package has been registered for the prefix
  287. *
  288. * @since 7.5.0
  289. * @see #addPackagePrefix(String, String)
  290. * @param prefix
  291. * the prefix to find a package for
  292. * @return the package prefix, or <code>null</code> if no package is
  293. * registered for the provided prefix
  294. */
  295. public String getPackage(String prefix) {
  296. return prefixToPackage.get(prefix);
  297. }
  298. /**
  299. * Returns the default instance for the given class. The instance must not
  300. * be modified by the caller.
  301. *
  302. * @param abstractComponent
  303. * @return the default instance for the given class. The return value must
  304. * not be modified by the caller
  305. */
  306. public <T> T getDefaultInstance(Component component) {
  307. // If the root is a @DesignRoot component, it can't use itself as a
  308. // reference or the written design will be empty
  309. // If the root component in some other way initializes itself in the
  310. // constructor
  311. if (getRootComponent() == component
  312. && component.getClass().isAnnotationPresent(DesignRoot.class)) {
  313. return (T) getDefaultInstance((Class<? extends Component>) component
  314. .getClass().getSuperclass());
  315. }
  316. return (T) getDefaultInstance(component.getClass());
  317. }
  318. private Component getDefaultInstance(
  319. Class<? extends Component> componentClass) {
  320. Component instance = instanceCache.get(componentClass);
  321. if (instance == null) {
  322. instance = instantiateClass(componentClass.getName());
  323. instanceCache.put(componentClass, instance);
  324. }
  325. return instance;
  326. }
  327. /**
  328. * Reads and stores the mappings from prefixes to package names from meta
  329. * tags located under <head> in the html document.
  330. */
  331. protected void readPackageMappings(Document doc) {
  332. Element head = doc.head();
  333. if (head == null) {
  334. return;
  335. }
  336. for (Node child : head.childNodes()) {
  337. if (child instanceof Element) {
  338. Element childElement = (Element) child;
  339. if ("meta".equals(childElement.tagName())) {
  340. Attributes attributes = childElement.attributes();
  341. if (attributes.hasKey("name")
  342. && attributes.hasKey("content")
  343. && "package-mapping".equals(attributes.get("name"))) {
  344. String contentString = attributes.get("content");
  345. String[] parts = contentString.split(":");
  346. if (parts.length != 2) {
  347. throw new DesignException("The meta tag '"
  348. + child.toString() + "' cannot be parsed.");
  349. }
  350. String prefixName = parts[0];
  351. String packageName = parts[1];
  352. addPackagePrefix(prefixName, packageName);
  353. }
  354. }
  355. }
  356. }
  357. }
  358. /**
  359. * Writes the package mappings (prefix -> package name) of this object to
  360. * the specified document.
  361. * <p>
  362. * The prefixes are stored as <meta> tags under <head> in the document.
  363. *
  364. * @param doc
  365. * the Jsoup document tree where the package mappings are written
  366. */
  367. public void writePackageMappings(Document doc) {
  368. Element head = doc.head();
  369. for (String prefix : getPackagePrefixes()) {
  370. // Only store the prefix-name mapping if it is not a default mapping
  371. // (such as "v" -> "com.vaadin.ui")
  372. if (defaultPrefixes.get(prefix) == null) {
  373. Node newNode = doc.createElement("meta");
  374. newNode.attr("name", "package-mapping");
  375. String prefixToPackageName = prefix + ":" + getPackage(prefix);
  376. newNode.attr("content", prefixToPackageName);
  377. head.appendChild(newNode);
  378. }
  379. }
  380. }
  381. /**
  382. * Creates an html tree node corresponding to the given element. Also
  383. * initializes its attributes by calling writeDesign. As a result of the
  384. * writeDesign() call, this method creates the entire subtree rooted at the
  385. * returned Node.
  386. *
  387. * @param childComponent
  388. * The component with state that is written in to the node
  389. * @return An html tree node corresponding to the given component. The tag
  390. * name of the created node is derived from the class name of
  391. * childComponent.
  392. */
  393. public Element createElement(Component childComponent) {
  394. ComponentMapper componentMapper = Design.getComponentMapper();
  395. String tagName = componentMapper.componentToTag(childComponent, this);
  396. Element newElement = doc.createElement(tagName);
  397. childComponent.writeDesign(newElement, this);
  398. // Handle the local id. Global id and caption should have been taken
  399. // care of by writeDesign.
  400. String localId = componentToLocalId.get(childComponent);
  401. if (localId != null) {
  402. newElement.attr(LOCAL_ID_ATTRIBUTE, localId);
  403. }
  404. return newElement;
  405. }
  406. /**
  407. * Reads the given design node and creates the corresponding component tree
  408. *
  409. * @param componentDesign
  410. * The design element containing the description of the component
  411. * to be created.
  412. * @return the root component of component tree
  413. */
  414. public Component readDesign(Element componentDesign) {
  415. // Create the component.
  416. Component component = instantiateComponent(componentDesign);
  417. readDesign(componentDesign, component);
  418. fireComponentCreatedEvent(componentToLocalId.get(component), component);
  419. return component;
  420. }
  421. /**
  422. *
  423. * Reads the given design node and populates the given component with the
  424. * corresponding component tree
  425. * <p>
  426. * Additionally registers the component id, local id and caption of the
  427. * given component and all its children in the context
  428. *
  429. * @param componentDesign
  430. * The design element containing the description of the component
  431. * to be created
  432. * @param component
  433. * The component which corresponds to the design element
  434. */
  435. public void readDesign(Element componentDesign, Component component) {
  436. component.readDesign(componentDesign, this);
  437. // Get the ids and the caption of the component and store them in the
  438. // maps of this design context.
  439. org.jsoup.nodes.Attributes attributes = componentDesign.attributes();
  440. // global id: only update the mapping, the id has already been set for
  441. // the component
  442. String id = component.getId();
  443. if (id != null && id.length() > 0) {
  444. boolean mappingExists = mapId(id, component);
  445. if (mappingExists) {
  446. throw new DesignException(
  447. "The following global id is not unique: " + id);
  448. }
  449. }
  450. // local id: this is not a property of a component, so need to fetch it
  451. // from the attributes of componentDesign
  452. if (attributes.hasKey(LOCAL_ID_ATTRIBUTE)) {
  453. String localId = attributes.get(LOCAL_ID_ATTRIBUTE);
  454. boolean mappingExists = setComponentLocalId(component, localId);
  455. if (mappingExists) {
  456. throw new DesignException(
  457. "the following local id is not unique: " + localId);
  458. }
  459. }
  460. // caption: a property of a component, possibly not unique
  461. String caption = component.getCaption();
  462. if (caption != null) {
  463. mapCaption(caption, component);
  464. }
  465. }
  466. /**
  467. * Creates a Component corresponding to the given node. Does not set the
  468. * attributes for the created object.
  469. *
  470. * @param node
  471. * a node of an html tree
  472. * @return a Component corresponding to node, with no attributes set.
  473. */
  474. private Component instantiateComponent(Node node) {
  475. String tag = node.nodeName();
  476. ComponentMapper componentMapper = Design.getComponentMapper();
  477. Component component = componentMapper.tagToComponent(tag,
  478. Design.getComponentFactory(), this);
  479. assert tag.equals(componentMapper.componentToTag(component, this));
  480. return component;
  481. }
  482. /**
  483. * Instantiates given class via ComponentFactory.
  484. *
  485. * @param qualifiedClassName
  486. * class name to instantiate
  487. * @return instance of a given class
  488. */
  489. private Component instantiateClass(String qualifiedClassName) {
  490. ComponentFactory factory = Design.getComponentFactory();
  491. Component component = factory.createComponent(qualifiedClassName, this);
  492. if (component == null) {
  493. throw new DesignException("Got unexpected null component from "
  494. + factory.getClass().getName() + " for class "
  495. + qualifiedClassName);
  496. }
  497. return component;
  498. }
  499. /**
  500. * Returns the root component of a created component hierarchy.
  501. *
  502. * @return the root component of the hierarchy
  503. */
  504. public Component getRootComponent() {
  505. return rootComponent;
  506. }
  507. /**
  508. * Sets the root component of a created component hierarchy.
  509. *
  510. * @param rootComponent
  511. * the root component of the hierarchy
  512. */
  513. public void setRootComponent(Component rootComponent) {
  514. this.rootComponent = rootComponent;
  515. }
  516. /**
  517. * Adds a component creation listener. The listener will be notified when
  518. * components are created while parsing a design template
  519. *
  520. * @param listener
  521. * the component creation listener to be added
  522. */
  523. public void addComponentCreationListener(ComponentCreationListener listener) {
  524. listeners.add(listener);
  525. }
  526. /**
  527. * Removes a component creation listener.
  528. *
  529. * @param listener
  530. * the component creation listener to be removed
  531. */
  532. public void removeComponentCreationListener(
  533. ComponentCreationListener listener) {
  534. listeners.remove(listener);
  535. }
  536. /**
  537. * Fires component creation event
  538. *
  539. * @param localId
  540. * localId of the component
  541. * @param component
  542. * the component that was created
  543. */
  544. private void fireComponentCreatedEvent(String localId, Component component) {
  545. ComponentCreatedEvent event = new ComponentCreatedEvent(localId,
  546. component);
  547. for (ComponentCreationListener listener : listeners) {
  548. listener.componentCreated(event);
  549. }
  550. }
  551. /**
  552. * Interface to be implemented by component creation listeners
  553. *
  554. * @author Vaadin Ltd
  555. */
  556. public interface ComponentCreationListener extends Serializable {
  557. /**
  558. * Called when component has been created in the design context
  559. *
  560. * @param event
  561. * the component creation event containing information on the
  562. * created component
  563. */
  564. public void componentCreated(ComponentCreatedEvent event);
  565. }
  566. /**
  567. * Component creation event that is fired when a component is created in the
  568. * context
  569. *
  570. * @author Vaadin Ltd
  571. */
  572. public class ComponentCreatedEvent implements Serializable {
  573. private String localId;
  574. private Component component;
  575. private DesignContext context;
  576. /**
  577. * Creates a new instance of ComponentCreatedEvent
  578. *
  579. * @param localId
  580. * the local id of the created component
  581. * @param component
  582. * the created component
  583. */
  584. private ComponentCreatedEvent(String localId, Component component) {
  585. this.localId = localId;
  586. this.component = component;
  587. context = DesignContext.this;
  588. }
  589. /**
  590. * Returns the local id of the created component or null if not exist
  591. *
  592. * @return the localId
  593. */
  594. public String getLocalId() {
  595. return localId;
  596. }
  597. /**
  598. * Returns the created component
  599. *
  600. * @return the component
  601. */
  602. public Component getComponent() {
  603. return component;
  604. }
  605. }
  606. /**
  607. * Helper method for component write implementors to determine whether their
  608. * children should be written out or not
  609. *
  610. * @param c
  611. * The component being written
  612. * @param defaultC
  613. * The default instance for the component
  614. * @return whether the children of c should be written
  615. */
  616. public boolean shouldWriteChildren(Component c, Component defaultC) {
  617. if (c == getRootComponent()) {
  618. // The root component should always write its children - otherwise
  619. // the result is empty
  620. return true;
  621. }
  622. if (defaultC instanceof HasComponents
  623. && ((HasComponents) defaultC).iterator().hasNext()) {
  624. // Easy version which assumes that this is a custom component if the
  625. // constructor adds children
  626. return false;
  627. }
  628. return true;
  629. }
  630. /**
  631. * Determines whether the container data of a component should be written
  632. * out by delegating to a {@link ShouldWriteDataDelegate}. The default
  633. * delegate assumes that all component data is provided by a data source
  634. * connected to a back end system and that the data should thus not be
  635. * written.
  636. *
  637. * @since 7.5.0
  638. * @see #setShouldWriteDataDelegate(ShouldWriteDataDelegate)
  639. * @param component
  640. * the component to check
  641. * @return <code>true</code> if container data should be written out for the
  642. * provided component; otherwise <code>false</code>.
  643. */
  644. public boolean shouldWriteData(Component component) {
  645. return getShouldWriteDataDelegate().shouldWriteData(component);
  646. }
  647. /**
  648. * Sets the delegate that determines whether the container data of a
  649. * component should be written out.
  650. *
  651. * @since 7.5.0
  652. * @see #shouldWriteChildren(Component, Component)
  653. * @see #getShouldWriteDataDelegate()
  654. * @param shouldWriteDataDelegate
  655. * the delegate to set, not <code>null</code>
  656. * @throws IllegalArgumentException
  657. * if the provided delegate is <code>null</code>
  658. */
  659. public void setShouldWriteDataDelegate(
  660. ShouldWriteDataDelegate shouldWriteDataDelegate) {
  661. if (shouldWriteDataDelegate == null) {
  662. throw new IllegalArgumentException("Delegate cannot be null");
  663. }
  664. this.shouldWriteDataDelegate = shouldWriteDataDelegate;
  665. }
  666. /**
  667. * Gets the delegate that determines whether the container data of a
  668. * component should be written out.
  669. *
  670. * @since 7.5.0
  671. * @see #setShouldWriteDataDelegate(ShouldWriteDataDelegate)
  672. * @see #shouldWriteChildren(Component, Component)
  673. * @return the shouldWriteDataDelegate the currently use delegate
  674. */
  675. public ShouldWriteDataDelegate getShouldWriteDataDelegate() {
  676. return shouldWriteDataDelegate;
  677. }
  678. }