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 27KB

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