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.

ComponentSizeValidator.java 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. /*
  2. * Copyright 2000-2016 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.PrintStream;
  18. import java.io.Serializable;
  19. import java.util.ArrayList;
  20. import java.util.HashMap;
  21. import java.util.Iterator;
  22. import java.util.LinkedList;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.Stack;
  26. import java.util.Vector;
  27. import java.util.logging.Level;
  28. import java.util.logging.Logger;
  29. import com.vaadin.server.Sizeable.Unit;
  30. import com.vaadin.ui.AbstractOrderedLayout;
  31. import com.vaadin.ui.AbstractSplitPanel;
  32. import com.vaadin.ui.Component;
  33. import com.vaadin.ui.ComponentContainer;
  34. import com.vaadin.ui.CustomComponent;
  35. import com.vaadin.ui.GridLayout;
  36. import com.vaadin.ui.GridLayout.Area;
  37. import com.vaadin.ui.HasComponents;
  38. import com.vaadin.ui.Panel;
  39. import com.vaadin.ui.TabSheet;
  40. import com.vaadin.ui.UI;
  41. import com.vaadin.ui.VerticalLayout;
  42. import com.vaadin.ui.Window;
  43. @SuppressWarnings({ "serial", "deprecation" })
  44. public class ComponentSizeValidator implements Serializable {
  45. private final static int LAYERS_SHOWN = 4;
  46. /**
  47. * Recursively checks given component and its subtree for invalid layout
  48. * setups. Prints errors to std err stream.
  49. *
  50. * @param component
  51. * component to check
  52. * @return set of first level errors found
  53. */
  54. public static List<InvalidLayout> validateComponentRelativeSizes(
  55. Component component, List<InvalidLayout> errors,
  56. InvalidLayout parent) {
  57. if (component != null) {
  58. boolean invalidHeight = !checkHeights(component);
  59. boolean invalidWidth = !checkWidths(component);
  60. if (invalidHeight || invalidWidth) {
  61. InvalidLayout error = new InvalidLayout(component,
  62. invalidHeight, invalidWidth);
  63. if (parent != null) {
  64. parent.addError(error);
  65. } else {
  66. if (errors == null) {
  67. errors = new LinkedList<>();
  68. }
  69. errors.add(error);
  70. }
  71. parent = error;
  72. }
  73. }
  74. if (component instanceof Panel) {
  75. Panel panel = (Panel) component;
  76. errors = validateComponentRelativeSizes(panel.getContent(), errors,
  77. parent);
  78. } else if (component instanceof ComponentContainer) {
  79. ComponentContainer lo = (ComponentContainer) component;
  80. Iterator<Component> it = lo.getComponentIterator();
  81. while (it.hasNext()) {
  82. errors = validateComponentRelativeSizes(it.next(), errors,
  83. parent);
  84. }
  85. } else if (isForm(component)) {
  86. HasComponents form = (HasComponents) component;
  87. for (Iterator<Component> iterator = form.iterator(); iterator
  88. .hasNext();) {
  89. Component child = iterator.next();
  90. errors = validateComponentRelativeSizes(child, errors, parent);
  91. }
  92. }
  93. return errors;
  94. }
  95. /**
  96. * Comparability form component which is defined in the different jar.
  97. *
  98. * TODO : Normally this logic shouldn't be here. But it means that the whole
  99. * this class has wrong design and impementation and should be refactored.
  100. */
  101. private static boolean isForm(Component component) {
  102. if (!(component instanceof HasComponents)) {
  103. return false;
  104. }
  105. Class<?> clazz = component.getClass();
  106. while (clazz != null) {
  107. if (component.getClass().getName()
  108. .equals("com.vaadin.v7.ui.Form")) {
  109. return true;
  110. }
  111. clazz = clazz.getSuperclass();
  112. }
  113. return false;
  114. }
  115. private static void printServerError(String msg,
  116. Stack<ComponentInfo> attributes, boolean widthError,
  117. PrintStream errorStream) {
  118. StringBuffer err = new StringBuffer();
  119. err.append("Vaadin DEBUG\n");
  120. StringBuilder indent = new StringBuilder("");
  121. ComponentInfo ci;
  122. if (attributes != null) {
  123. while (attributes.size() > LAYERS_SHOWN) {
  124. attributes.pop();
  125. }
  126. while (!attributes.empty()) {
  127. ci = attributes.pop();
  128. showComponent(ci.component, ci.info, err, indent, widthError);
  129. }
  130. }
  131. err.append("Layout problem detected: ");
  132. err.append(msg);
  133. err.append("\n");
  134. err.append(
  135. "Relative sizes were replaced by undefined sizes, components may not render as expected.\n");
  136. errorStream.println(err);
  137. }
  138. public static boolean checkHeights(Component component) {
  139. try {
  140. if (!hasRelativeHeight(component)) {
  141. return true;
  142. }
  143. if (component instanceof Window) {
  144. return true;
  145. }
  146. if (component.getParent() == null) {
  147. return true;
  148. }
  149. return parentCanDefineHeight(component);
  150. } catch (Exception e) {
  151. getLogger().log(Level.FINER,
  152. "An exception occurred while validating sizes.", e);
  153. return true;
  154. }
  155. }
  156. public static boolean checkWidths(Component component) {
  157. try {
  158. if (!hasRelativeWidth(component)) {
  159. return true;
  160. }
  161. if (component instanceof Window) {
  162. return true;
  163. }
  164. if (component.getParent() == null) {
  165. return true;
  166. }
  167. return parentCanDefineWidth(component);
  168. } catch (Exception e) {
  169. getLogger().log(Level.FINER,
  170. "An exception occurred while validating sizes.", e);
  171. return true;
  172. }
  173. }
  174. public static class InvalidLayout implements Serializable {
  175. private final Component component;
  176. private final boolean invalidHeight;
  177. private final boolean invalidWidth;
  178. private final Vector<InvalidLayout> subErrors = new Vector<>();
  179. public InvalidLayout(Component component, boolean height,
  180. boolean width) {
  181. this.component = component;
  182. invalidHeight = height;
  183. invalidWidth = width;
  184. }
  185. public void addError(InvalidLayout error) {
  186. subErrors.add(error);
  187. }
  188. public void reportErrors(StringBuilder clientJSON,
  189. PrintStream serverErrorStream) {
  190. clientJSON.append("{");
  191. Component parent = component.getParent();
  192. String paintableId = component.getConnectorId();
  193. clientJSON.append("\"id\":\"" + paintableId + "\"");
  194. if (invalidHeight) {
  195. Stack<ComponentInfo> attributes = null;
  196. String msg = "";
  197. // set proper error messages
  198. if (parent instanceof AbstractOrderedLayout) {
  199. AbstractOrderedLayout ol = (AbstractOrderedLayout) parent;
  200. boolean vertical = false;
  201. if (ol instanceof VerticalLayout) {
  202. vertical = true;
  203. }
  204. if (vertical) {
  205. msg = "Component with relative height inside a VerticalLayout with no height defined.";
  206. attributes = getHeightAttributes(component);
  207. } else {
  208. msg = "At least one of a HorizontalLayout's components must have non relative height if the height of the layout is not defined";
  209. attributes = getHeightAttributes(component);
  210. }
  211. } else if (parent instanceof GridLayout) {
  212. msg = "At least one of the GridLayout's components in each row should have non relative height if the height of the layout is not defined.";
  213. attributes = getHeightAttributes(component);
  214. } else {
  215. // default error for non sized parent issue
  216. msg = "A component with relative height needs a parent with defined height.";
  217. attributes = getHeightAttributes(component);
  218. }
  219. printServerError(msg, attributes, false, serverErrorStream);
  220. clientJSON.append(",\"heightMsg\":\"" + msg + "\"");
  221. }
  222. if (invalidWidth) {
  223. Stack<ComponentInfo> attributes = null;
  224. String msg = "";
  225. if (parent instanceof AbstractOrderedLayout) {
  226. AbstractOrderedLayout ol = (AbstractOrderedLayout) parent;
  227. boolean horizontal = true;
  228. if (ol instanceof VerticalLayout) {
  229. horizontal = false;
  230. }
  231. if (horizontal) {
  232. msg = "Component with relative width inside a HorizontalLayout with no width defined";
  233. attributes = getWidthAttributes(component);
  234. } else {
  235. msg = "At least one of a VerticalLayout's components must have non relative width if the width of the layout is not defined";
  236. attributes = getWidthAttributes(component);
  237. }
  238. } else if (parent instanceof GridLayout) {
  239. msg = "At least one of the GridLayout's components in each column should have non relative width if the width of the layout is not defined.";
  240. attributes = getWidthAttributes(component);
  241. } else {
  242. // default error for non sized parent issue
  243. msg = "A component with relative width needs a parent with defined width.";
  244. attributes = getWidthAttributes(component);
  245. }
  246. clientJSON.append(",\"widthMsg\":\"" + msg + "\"");
  247. printServerError(msg, attributes, true, serverErrorStream);
  248. }
  249. if (subErrors.size() > 0) {
  250. serverErrorStream.println("Sub errors >>");
  251. clientJSON.append(", \"subErrors\" : [");
  252. boolean first = true;
  253. for (InvalidLayout subError : subErrors) {
  254. if (!first) {
  255. clientJSON.append(",");
  256. } else {
  257. first = false;
  258. }
  259. subError.reportErrors(clientJSON, serverErrorStream);
  260. }
  261. clientJSON.append("]");
  262. serverErrorStream.println("<< Sub erros");
  263. }
  264. clientJSON.append("}");
  265. }
  266. }
  267. private static class ComponentInfo implements Serializable {
  268. Component component;
  269. String info;
  270. public ComponentInfo(Component component, String info) {
  271. this.component = component;
  272. this.info = info;
  273. }
  274. }
  275. private static Stack<ComponentInfo> getHeightAttributes(
  276. Component component) {
  277. Stack<ComponentInfo> attributes = new Stack<>();
  278. attributes
  279. .add(new ComponentInfo(component, getHeightString(component)));
  280. Component parent = component.getParent();
  281. attributes.add(new ComponentInfo(parent, getHeightString(parent)));
  282. while ((parent = parent.getParent()) != null) {
  283. attributes.add(new ComponentInfo(parent, getHeightString(parent)));
  284. }
  285. return attributes;
  286. }
  287. private static Stack<ComponentInfo> getWidthAttributes(
  288. Component component) {
  289. Stack<ComponentInfo> attributes = new Stack<>();
  290. attributes.add(new ComponentInfo(component, getWidthString(component)));
  291. Component parent = component.getParent();
  292. attributes.add(new ComponentInfo(parent, getWidthString(parent)));
  293. while ((parent = parent.getParent()) != null) {
  294. attributes.add(new ComponentInfo(parent, getWidthString(parent)));
  295. }
  296. return attributes;
  297. }
  298. private static String getWidthString(Component component) {
  299. String width = "width: ";
  300. if (hasRelativeWidth(component)) {
  301. width += "RELATIVE, " + component.getWidth() + " %";
  302. } else if (component instanceof Window
  303. && component.getParent() == null) {
  304. width += "MAIN WINDOW";
  305. } else if (component.getWidth() >= 0) {
  306. width += "ABSOLUTE, " + component.getWidth() + " "
  307. + component.getWidthUnits().getSymbol();
  308. } else {
  309. width += "UNDEFINED";
  310. }
  311. return width;
  312. }
  313. private static String getHeightString(Component component) {
  314. String height = "height: ";
  315. if (hasRelativeHeight(component)) {
  316. height += "RELATIVE, " + component.getHeight() + " %";
  317. } else if (component instanceof Window
  318. && component.getParent() == null) {
  319. height += "MAIN WINDOW";
  320. } else if (component.getHeight() > 0) {
  321. height += "ABSOLUTE, " + component.getHeight() + " "
  322. + component.getHeightUnits().getSymbol();
  323. } else {
  324. height += "UNDEFINED";
  325. }
  326. return height;
  327. }
  328. private static void showComponent(Component component, String attribute,
  329. StringBuffer err, StringBuilder indent, boolean widthError) {
  330. FileLocation createLoc = creationLocations.get(component);
  331. FileLocation sizeLoc;
  332. if (widthError) {
  333. sizeLoc = widthLocations.get(component);
  334. } else {
  335. sizeLoc = heightLocations.get(component);
  336. }
  337. err.append(indent);
  338. indent.append(" ");
  339. err.append("- ");
  340. err.append(component.getClass().getSimpleName());
  341. err.append("/").append(Integer.toHexString(component.hashCode()));
  342. if (component.getCaption() != null) {
  343. err.append(" \"");
  344. err.append(component.getCaption());
  345. err.append("\"");
  346. }
  347. if (component.getId() != null) {
  348. err.append(" id: ");
  349. err.append(component.getId());
  350. }
  351. if (createLoc != null) {
  352. err.append(", created at (" + createLoc.file + ":"
  353. + createLoc.lineNumber + ")");
  354. }
  355. if (attribute != null) {
  356. err.append(" (");
  357. err.append(attribute);
  358. if (sizeLoc != null) {
  359. err.append(", set at (" + sizeLoc.file + ":"
  360. + sizeLoc.lineNumber + ")");
  361. }
  362. err.append(")");
  363. }
  364. err.append("\n");
  365. }
  366. private static boolean hasNonRelativeHeightComponent(
  367. AbstractOrderedLayout ol) {
  368. Iterator<Component> it = ol.getComponentIterator();
  369. while (it.hasNext()) {
  370. if (!hasRelativeHeight(it.next())) {
  371. return true;
  372. }
  373. }
  374. return false;
  375. }
  376. public static boolean parentCanDefineHeight(Component component) {
  377. Component parent = component.getParent();
  378. if (parent == null) {
  379. // main window, valid situation
  380. return true;
  381. }
  382. if (parent.getHeight() < 0) {
  383. // Undefined height
  384. if (parent instanceof Window) {
  385. // Sub window with undefined size has a min-height
  386. return true;
  387. }
  388. if (parent instanceof AbstractOrderedLayout) {
  389. boolean horizontal = true;
  390. if (parent instanceof VerticalLayout) {
  391. horizontal = false;
  392. }
  393. if (horizontal && hasNonRelativeHeightComponent(
  394. (AbstractOrderedLayout) parent)) {
  395. return true;
  396. } else {
  397. return false;
  398. }
  399. } else if (parent instanceof GridLayout) {
  400. GridLayout gl = (GridLayout) parent;
  401. Area componentArea = gl.getComponentArea(component);
  402. boolean rowHasHeight = false;
  403. for (int row = componentArea.getRow1(); !rowHasHeight
  404. && row <= componentArea.getRow2(); row++) {
  405. for (int column = 0; !rowHasHeight
  406. && column < gl.getColumns(); column++) {
  407. Component c = gl.getComponent(column, row);
  408. if (c != null) {
  409. rowHasHeight = !hasRelativeHeight(c);
  410. }
  411. }
  412. }
  413. if (!rowHasHeight) {
  414. return false;
  415. } else {
  416. // Other components define row height
  417. return true;
  418. }
  419. } else if (isForm(parent)) {
  420. /*
  421. * If some other part of the form is not relative it determines
  422. * the component width
  423. */
  424. return formHasNonRelativeWidthComponent(parent);
  425. }
  426. if (parent instanceof Panel || parent instanceof AbstractSplitPanel
  427. || parent instanceof TabSheet
  428. || parent instanceof CustomComponent) {
  429. // height undefined, we know how how component works and no
  430. // exceptions
  431. // TODO horiz SplitPanel ??
  432. return false;
  433. } else {
  434. // We cannot generally know if undefined component can serve
  435. // space for children (like CustomLayout or component built by
  436. // third party) so we assume they can
  437. return true;
  438. }
  439. } else if (hasRelativeHeight(parent)) {
  440. // Relative height
  441. if (parent.getParent() != null) {
  442. return parentCanDefineHeight(parent);
  443. } else {
  444. return true;
  445. }
  446. } else {
  447. // Absolute height
  448. return true;
  449. }
  450. }
  451. /**
  452. * Comparability form component which is defined in the different jar.
  453. *
  454. * TODO : Normally this logic shouldn't be here. But it means that the whole
  455. * this class has wrong design and impementation and should be refactored.
  456. */
  457. private static boolean formHasNonRelativeWidthComponent(Component form) {
  458. HasComponents parent = (HasComponents) form;
  459. for (Iterator<Component> iterator = parent.iterator(); iterator
  460. .hasNext();) {
  461. if (!hasRelativeWidth(iterator.next())) {
  462. return true;
  463. }
  464. }
  465. return false;
  466. }
  467. private static boolean hasRelativeHeight(Component component) {
  468. return (component.getHeightUnits() == Unit.PERCENTAGE
  469. && component.getHeight() > 0);
  470. }
  471. private static boolean hasNonRelativeWidthComponent(
  472. AbstractOrderedLayout ol) {
  473. Iterator<Component> it = ol.getComponentIterator();
  474. while (it.hasNext()) {
  475. if (!hasRelativeWidth(it.next())) {
  476. return true;
  477. }
  478. }
  479. return false;
  480. }
  481. private static boolean hasRelativeWidth(Component paintable) {
  482. return paintable.getWidth() > 0
  483. && paintable.getWidthUnits() == Unit.PERCENTAGE;
  484. }
  485. public static boolean parentCanDefineWidth(Component component) {
  486. Component parent = component.getParent();
  487. if (parent == null) {
  488. // main window, valid situation
  489. return true;
  490. }
  491. if (parent instanceof Window) {
  492. // Sub window with undefined size has a min-width
  493. return true;
  494. }
  495. if (parent.getWidth() < 0) {
  496. // Undefined width
  497. if (parent instanceof AbstractOrderedLayout) {
  498. AbstractOrderedLayout ol = (AbstractOrderedLayout) parent;
  499. boolean horizontal = true;
  500. if (ol instanceof VerticalLayout) {
  501. horizontal = false;
  502. }
  503. if (!horizontal && hasNonRelativeWidthComponent(ol)) {
  504. // valid situation, other components defined width
  505. return true;
  506. } else {
  507. return false;
  508. }
  509. } else if (parent instanceof GridLayout) {
  510. GridLayout gl = (GridLayout) parent;
  511. Area componentArea = gl.getComponentArea(component);
  512. boolean columnHasWidth = false;
  513. for (int col = componentArea.getColumn1(); !columnHasWidth
  514. && col <= componentArea.getColumn2(); col++) {
  515. for (int row = 0; !columnHasWidth
  516. && row < gl.getRows(); row++) {
  517. Component c = gl.getComponent(col, row);
  518. if (c != null) {
  519. columnHasWidth = !hasRelativeWidth(c);
  520. }
  521. }
  522. }
  523. if (!columnHasWidth) {
  524. return false;
  525. } else {
  526. // Other components define column width
  527. return true;
  528. }
  529. } else if (parent instanceof AbstractSplitPanel
  530. || parent instanceof TabSheet
  531. || parent instanceof CustomComponent) {
  532. // FIXME Could we use com.vaadin package name here and
  533. // fail for all component containers?
  534. // FIXME Actually this should be moved to containers so it can
  535. // be implemented for custom containers
  536. // TODO vertical splitpanel with another non relative component?
  537. return false;
  538. } else if (parent instanceof Window) {
  539. // Sub window can define width based on caption
  540. if (parent.getCaption() != null
  541. && !parent.getCaption().equals("")) {
  542. return true;
  543. } else {
  544. return false;
  545. }
  546. } else if (parent instanceof Panel) {
  547. // TODO Panel should be able to define width based on caption
  548. return false;
  549. } else {
  550. return true;
  551. }
  552. } else if (hasRelativeWidth(parent)) {
  553. // Relative width
  554. if (parent.getParent() == null) {
  555. return true;
  556. }
  557. return parentCanDefineWidth(parent);
  558. } else {
  559. return true;
  560. }
  561. }
  562. private static Map<Object, FileLocation> creationLocations = new HashMap<>();
  563. private static Map<Object, FileLocation> widthLocations = new HashMap<>();
  564. private static Map<Object, FileLocation> heightLocations = new HashMap<>();
  565. public static class FileLocation implements Serializable {
  566. public String method;
  567. public String file;
  568. public String className;
  569. public String classNameSimple;
  570. public int lineNumber;
  571. public FileLocation(StackTraceElement traceElement) {
  572. file = traceElement.getFileName();
  573. className = traceElement.getClassName();
  574. classNameSimple = className
  575. .substring(className.lastIndexOf('.') + 1);
  576. lineNumber = traceElement.getLineNumber();
  577. method = traceElement.getMethodName();
  578. }
  579. }
  580. public static void setCreationLocation(Object object) {
  581. setLocation(creationLocations, object);
  582. }
  583. public static void setWidthLocation(Object object) {
  584. setLocation(widthLocations, object);
  585. }
  586. public static void setHeightLocation(Object object) {
  587. setLocation(heightLocations, object);
  588. }
  589. private static void setLocation(Map<Object, FileLocation> map,
  590. Object object) {
  591. StackTraceElement[] traceLines = Thread.currentThread().getStackTrace();
  592. for (StackTraceElement traceElement : traceLines) {
  593. Class<?> cls;
  594. try {
  595. String className = traceElement.getClassName();
  596. if (className.startsWith("java.")
  597. || className.startsWith("sun.")) {
  598. continue;
  599. }
  600. cls = Class.forName(className);
  601. if (cls == ComponentSizeValidator.class
  602. || cls == Thread.class) {
  603. continue;
  604. }
  605. if (Component.class.isAssignableFrom(cls)
  606. && !CustomComponent.class.isAssignableFrom(cls)) {
  607. continue;
  608. }
  609. FileLocation cl = new FileLocation(traceElement);
  610. map.put(object, cl);
  611. return;
  612. } catch (Exception e) {
  613. getLogger().log(Level.FINER,
  614. "An exception occurred while validating sizes.", e);
  615. }
  616. }
  617. }
  618. private static Logger getLogger() {
  619. return Logger.getLogger(ComponentSizeValidator.class.getName());
  620. }
  621. /**
  622. * Validates the layout and returns a collection of errors
  623. *
  624. * @since 7.1
  625. * @param ui
  626. * The UI to validate
  627. * @return A collection of errors. An empty collection if there are no
  628. * errors.
  629. */
  630. public static List<InvalidLayout> validateLayouts(UI ui) {
  631. List<InvalidLayout> invalidRelativeSizes = ComponentSizeValidator
  632. .validateComponentRelativeSizes(ui.getContent(),
  633. new ArrayList<ComponentSizeValidator.InvalidLayout>(),
  634. null);
  635. // Also check any existing subwindows
  636. if (ui.getWindows() != null) {
  637. for (Window subWindow : ui.getWindows()) {
  638. invalidRelativeSizes = ComponentSizeValidator
  639. .validateComponentRelativeSizes(subWindow.getContent(),
  640. invalidRelativeSizes, null);
  641. }
  642. }
  643. return invalidRelativeSizes;
  644. }
  645. }