001/*
002 * Copyright (c) 2004-2007 by Michael Connor. All Rights Reserved.
003 *
004 * Redistribution and use in source and binary forms, with or without
005 * modification, are permitted provided that the following conditions are met:
006 *
007 *  o Redistributions of source code must retain the above copyright notice,
008 *    this list of conditions and the following disclaimer.
009 *
010 *  o Redistributions in binary form must reproduce the above copyright notice,
011 *    this list of conditions and the following disclaimer in the documentation
012 *    and/or other materials provided with the distribution.
013 *
014 *  o Neither the name of FormLayoutBuilder or Michael Connor nor the names of
015 *    its contributors may be used to endorse or promote products derived
016 *    from this software without specific prior written permission.
017 *
018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.mlc.swing.layout;
031
032import java.awt.Component;
033import java.awt.Container;
034import java.awt.Insets;
035import java.beans.Statement;
036import java.beans.XMLDecoder;
037import java.beans.XMLEncoder;
038import java.io.ByteArrayInputStream;
039import java.io.ByteArrayOutputStream;
040import java.io.InputStream;
041import java.util.ArrayList;
042import java.util.HashMap;
043import java.util.HashSet;
044import java.util.LinkedHashMap;
045import java.util.List;
046import java.util.Locale;
047import java.util.Map;
048import java.util.Set;
049
050import javax.swing.JButton;
051import javax.swing.JCheckBox;
052import javax.swing.JLabel;
053import javax.swing.JRadioButton;
054import javax.swing.JToggleButton;
055import javax.xml.parsers.DocumentBuilder;
056import javax.xml.parsers.DocumentBuilderFactory;
057import javax.xml.transform.OutputKeys;
058import javax.xml.transform.Transformer;
059import javax.xml.transform.TransformerFactory;
060import javax.xml.transform.dom.DOMSource;
061import javax.xml.transform.stream.StreamResult;
062
063import org.w3c.dom.Document;
064import org.w3c.dom.NamedNodeMap;
065import org.w3c.dom.Node;
066import org.w3c.dom.NodeList;
067
068import com.jgoodies.forms.layout.CellConstraints;
069
070/**
071 * This class handles the serialization and deserialization of the xml files
072 * that we are using to store the layout constraints.
073 *
074 * <p>In the consuming program, the use of this class might look like:
075<br><code>
076InputStream constraints = this.getClass().getResourceAsStream(xmlFile);
077LayoutConstraintsManager layoutConstraintsManager =
078        LayoutConstraintsManager.getLayoutConstraintsManager(constraints);
079LayoutManager layout = layoutConstraintsManager.createLayout("panel", this);
080this.setLayout(layout);
081</code>
082 *
083 * <p>[I'm sure there are more
084 * elegant ways of handling this (like JAXB) or some other mapping software but
085 * this is simple, it works, and we don't have to package a bunch of other
086 * software or files.]
087 *
088 * @author Michael Connor
089@version $Id$
090@since Ptolemy II 8.0
091 */
092public class LayoutConstraintsManager {
093    String defaultColumnSpecs = "right:max(30dlu;pref),3dlu,80dlu,10dlu,right:max(30dlu;pref),3dlu,80dlu,1dlu:grow";
094
095    String defaultRowSpecs = "pref,3dlu,pref,3dlu,pref,3dlu,pref,3dlu,pref,3dlu,pref,3dlu,pref,3dlu,pref,3dlu,pref,3dlu,pref,3dlu,pref:grow";
096
097    static Set<Class> textComponents = new HashSet<Class>();
098
099    static {
100        textComponents.add(JButton.class);
101        textComponents.add(JCheckBox.class);
102        textComponents.add(JRadioButton.class);
103        textComponents.add(JToggleButton.class);
104        textComponents.add(JLabel.class);
105    }
106
107    Map<ContainerLayout, Container> containers = new HashMap<ContainerLayout, Container>();
108
109    List<ContainerLayout> layouts = new ArrayList<ContainerLayout>();
110
111    /**
112     * This method will create a LayoutConstraintsManager with default JGoodies
113     * row and column specs that are common in applications. The user can then
114     * manipulate these default specs using the LayoutFrame to fine tune the specs
115     * to be whatever they want.
116     */
117    public LayoutConstraintsManager() {
118    }
119
120    /**
121     * This method will create a LayoutConstraintsManager with the JGoodies specs
122     * provided as default
123     */
124    public LayoutConstraintsManager(String defaultColumnSpecs,
125            String defaultRowSpecs) {
126        this.defaultColumnSpecs = defaultColumnSpecs;
127        this.defaultRowSpecs = defaultRowSpecs;
128    }
129
130    public List<ContainerLayout> getLayouts() {
131        List<ContainerLayout> list = new ArrayList<ContainerLayout>(
132                layouts.size());
133        list.addAll(layouts);
134        return list;
135    }
136
137    /**
138     * This method will build a layout from the xml file based on the name and
139     * call setLayout on the container passed in.
140     */
141    public void setLayout(String name, Container container) {
142        ContainerLayout containerLayout = getLayout(name);
143
144        if (containerLayout == null) {
145            containerLayout = new ContainerLayout(name, defaultColumnSpecs,
146                    defaultRowSpecs);
147            layouts.add(containerLayout);
148        }
149        //    else
150        //      containers.remove(containerLayout);
151
152        container.setLayout(containerLayout);
153        containers.put(containerLayout, container);
154    }
155
156    /**
157     * This method creates a layout by first trying to look in memory to see if a
158     * layout has been defined with the given name. If a layout exists, it is
159     * returned. Note that when I say in memory that probably means that it was
160     * defined in the xml file. If one doesn't exist, a layout with what I
161     * consider relatively generic constraints will be created and returned. The
162     * reason this method requires the container is because in the case where you
163     * are trying to layout the container visually, I need to be able to get a
164     * handle on the container so I can make calls to add components to it during
165     * interactive layout. This will not be touched at runtime if you are not
166     * doing anything interactively. This method should be seen as a replacement
167     * for LayoutConstraintsManager.setLayout(String name, Container container) as
168     * it's more natural to set the layout on the container yourself.
169     */
170    public ContainerLayout createLayout(String name, Container container) {
171        ContainerLayout containerLayout = getLayout(name);
172
173        if (containerLayout == null) {
174            containerLayout = new ContainerLayout(name, defaultColumnSpecs,
175                    defaultRowSpecs);
176            layouts.add(containerLayout);
177        }
178
179        containers.put(containerLayout, container);
180        return containerLayout;
181    }
182
183    public Container getContainer(ContainerLayout layout) {
184        return containers.get(layout);
185    }
186
187    private ContainerLayout getLayout(String name) {
188        for (int i = 0; i < layouts.size(); i++) {
189            if (layouts.get(i).getName().equals(name)) {
190                return layouts.get(i);
191            }
192        }
193
194        return null;
195    }
196
197    // KBR NYI
198    //  public List<String> getContainerNames()
199    //  {
200    //    List<String> names = new ArrayList<String>();
201    //
202    //    return names;
203    //  }
204
205    public ContainerLayout getContainerLayout(String containerName) {
206        ContainerLayout layout = getLayout(containerName);
207        return layout;
208    }
209
210    public void removeLayout(ContainerLayout containerLayout) {
211        layouts.remove(containerLayout);
212    }
213
214    public void addLayout(ContainerLayout containerLayout) {
215        layouts.add(containerLayout);
216    }
217
218    /**
219      Get an XML representation of the FormLayout constraints for all containers
220      in this manager.
221     */
222    public String getXML() {
223        StringBuffer xml = new StringBuffer(
224                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n");
225        xml.append("<containers>\n");
226
227        for (int index = 0; index < layouts.size(); index++) {
228            ContainerLayout layout = layouts.get(index);
229            LinkedHashMap<String, CellConstraints> constraintMap = layout
230                    .getCellConstraints();
231
232            xml.append("    <container name=\"" + layout.getName() + "\"\n");
233            xml.append("               columnSpecs=\""
234                    + layout.getColumnSpecsString() + "\"\n");
235            xml.append("               rowSpecs=\"" + layout.getRowSpecsString()
236                    + "\">\n");
237
238            for (Object element : constraintMap.keySet()) {
239                String componentName = (String) element;
240                CellConstraints constraints = constraintMap.get(componentName);
241                xml.append("        <cellconstraints ");
242                xml.append("name=\"" + componentName + "\" ");
243                xml.append("gridX=\"" + constraints.gridX + "\" ");
244                xml.append("gridY=\"" + constraints.gridY + "\" ");
245                xml.append("gridWidth=\"" + constraints.gridWidth + "\" ");
246                xml.append("gridHeight=\"" + constraints.gridHeight + "\" ");
247                xml.append("horizontalAlignment=\""
248                        + getAlignment(constraints.hAlign) + "\" ");
249                xml.append("verticalAlignment=\""
250                        + getAlignment(constraints.vAlign) + "\" ");
251                xml.append("topInset=\"" + constraints.insets.top + "\" ");
252                xml.append(
253                        "bottomInset=\"" + constraints.insets.bottom + "\" ");
254                xml.append("rightInset=\"" + constraints.insets.right + "\" ");
255                xml.append("leftInset=\"" + constraints.insets.left + "\"/>\n");
256            }
257
258            for (Object element : constraintMap.keySet()) {
259                String componentName = (String) element;
260                Component component = layout.getComponentByName(componentName);
261
262                if (component != null) {
263                    Map<String, Object> customProperties = layout
264                            .getCustomProperties(componentName);
265
266                    boolean hasProperties = false;
267                    boolean isTextComponent = isTextComponent(component);
268                    // we need to look through these and see if we have
269                    // any valid properties. the text props don't count
270                    // for controls like JLabel, JButton, etc. because
271                    // we'll put those in the constructor.
272
273                    // EAL: In Ptolemy, we need text in the properties
274                    // fields. So remove this check below, and force
275                    // isTextComponent to false.
276                    isTextComponent = false;
277
278                    //for (String propertyName : customProperties.keySet()) {
279                    //    if ((!isTextComponent) /* EAL: || (!propertyName.equals("text")) */) {
280                    //        hasProperties = true;
281                    //        break;
282                    //    }
283                    //}
284                    if (!customProperties.keySet().isEmpty()) {
285                        hasProperties = true;
286                    }
287
288                    if (hasProperties) {
289                        xml.append("\n        <properties component=\""
290                                + componentName + "\">\n");
291                        for (String propertyName : customProperties.keySet()) {
292
293                            if (isTextComponent
294                                    && propertyName.equals("text")) {
295                                break;
296                            }
297
298                            ByteArrayOutputStream stream = new ByteArrayOutputStream();
299                            XMLEncoder xmlEncoder = new XMLEncoder(stream);
300                            xmlEncoder.setOwner(component);
301
302                            String methodName = "set"
303                                    + propertyName.substring(0, 1)
304                                            .toUpperCase(Locale.getDefault())
305                                    + (propertyName.length() > 1
306                                            ? propertyName.substring(1)
307                                            : "");
308                            xmlEncoder.writeStatement(new Statement(xmlEncoder,
309                                    methodName, new Object[] { customProperties
310                                            .get(propertyName) }));
311                            xmlEncoder.close();
312
313                            String propertyXml = new String(
314                                    stream.toByteArray());
315                            int voidStart = propertyXml.indexOf("<void");
316                            int voidEnd = propertyXml.indexOf(">", voidStart);
317                            int end = propertyXml.lastIndexOf("</void>");
318                            String xmlWithoutDec = propertyXml
319                                    .substring(voidEnd + 1, end);
320                            xmlWithoutDec = xmlWithoutDec.trim();
321                            String indented = "          " + xmlWithoutDec
322                                    .replaceAll("\n", "\n        ");
323                            xml.append("         <property name=\""
324                                    + propertyName + "\">");
325                            xml.append(indented);
326                            xml.append("</property>\n");
327                        }
328
329                        xml.append("        </properties>\n");
330                    }
331                }
332            }
333
334            xml.append("    </container>\n");
335        }
336
337        xml.append("</containers>\n");
338        return xml.toString();
339    }
340
341    public static boolean isTextComponent(Component component) {
342        for (Class clazz : textComponents) {
343            if (clazz.isAssignableFrom(component.getClass())) {
344                return true;
345            }
346        }
347        return false;
348    }
349
350    private static String createString(NodeList childNodes) {
351
352        try {
353            ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
354            TransformerFactory tFactory = TransformerFactory.newInstance();
355            Transformer transformer = tFactory.newTransformer();
356            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,
357                    "yes");
358
359            for (int i = 0; i < childNodes.getLength(); i++) {
360                Node child = childNodes.item(i);
361                DOMSource source = new DOMSource(child);
362                StreamResult result = new StreamResult(byteStream);
363                transformer.transform(source, result);
364            }
365            return new String(byteStream.toByteArray());
366        } catch (Exception e) {
367            e.printStackTrace();
368            throw new RuntimeException("unexpected error");
369        }
370    }
371
372    public static final String DEFAULT = "default";
373
374    public static final String FILL = "fill";
375
376    public static final String CENTER = "center";
377
378    public static final String LEFT = "left";
379
380    public static final String RIGHT = "right";
381
382    public static final String TOP = "top";
383
384    public static final String BOTTOM = "bottom";
385
386    /**
387     * Translates an alignment value to a string.
388     */
389    public static String getAlignment(CellConstraints.Alignment alignment) {
390        String value = null;
391
392        if (alignment == CellConstraints.DEFAULT) {
393            value = DEFAULT;
394        } else if (alignment == CellConstraints.FILL) {
395            value = FILL;
396        } else if (alignment == CellConstraints.CENTER) {
397            value = CENTER;
398        } else if (alignment == CellConstraints.LEFT) {
399            value = LEFT;
400        } else if (alignment == CellConstraints.RIGHT) {
401            value = RIGHT;
402        } else if (alignment == CellConstraints.TOP) {
403            value = TOP;
404        } else if (alignment == CellConstraints.BOTTOM) {
405            value = BOTTOM;
406        }
407
408        if (value == null) {
409            throw new RuntimeException("Unknown alignment type");
410        } else {
411            return value;
412        }
413    }
414
415    /**
416     * Translates a string to an alignment value.
417     */
418    public static CellConstraints.Alignment getAlignment(String value) {
419        CellConstraints.Alignment alignment = null;
420
421        if (value.equalsIgnoreCase(DEFAULT)) {
422            alignment = CellConstraints.DEFAULT;
423        } else if (value.equalsIgnoreCase(FILL)) {
424            alignment = CellConstraints.FILL;
425        } else if (value.equalsIgnoreCase(CENTER)) {
426            alignment = CellConstraints.CENTER;
427        } else if (value.equalsIgnoreCase(LEFT)) {
428            alignment = CellConstraints.LEFT;
429        } else if (value.equalsIgnoreCase(RIGHT)) {
430            alignment = CellConstraints.RIGHT;
431        } else if (value.equalsIgnoreCase(TOP)) {
432            alignment = CellConstraints.TOP;
433        } else if (value.equalsIgnoreCase(BOTTOM)) {
434            alignment = CellConstraints.BOTTOM;
435        } else {
436            throw new RuntimeException("Invalid alignment");
437        }
438
439        return alignment;
440    }
441
442    /**
443     * Returns a LayoutConstraintsManager based on an input stream for an xml
444     * file. The root node in the xml file should be called <code>containers</code> and should
445     * adhere to the xml format for this tool.
446     */
447    public static LayoutConstraintsManager getLayoutConstraintsManager(
448            InputStream stream) {
449        Document dataDocument = null;
450
451        try {
452            DocumentBuilder documentBuilder = DocumentBuilderFactory
453                    .newInstance().newDocumentBuilder();
454            dataDocument = documentBuilder.parse(stream);
455        } catch (Exception e) {
456            e.printStackTrace();
457            throw new RuntimeException("Unable to create DocumentBuilder", e);
458        }
459
460        Node root = dataDocument.getDocumentElement();
461        return getLayoutConstraintsManager(root);
462    }
463
464    /**
465     * Returns a layout constraints manager given a containers node. This will
466     * enable you to keep a lot of different constraints in a single file or at
467     * least provide a little more flexibility.
468     */
469    public static LayoutConstraintsManager getLayoutConstraintsManager(
470            Node containersNode) {
471
472        if (!containersNode.getNodeName().equals("containers")) {
473            throw new RuntimeException("Expected a node named containers");
474        }
475
476        LayoutConstraintsManager layoutConstraintsManager = new LayoutConstraintsManager();
477        Node[] containerNodes = getNodesNamed(containersNode, "container");
478
479        for (Node containerNode : containerNodes) {
480            Map<String, String> containerAttributes = getAttributeMap(
481                    containerNode);
482            String containerName = containerAttributes.get("name");
483            if (containerName == null) {
484                throw new RuntimeException(
485                        "Container must have a name attribute");
486            }
487            String columnSpecs = containerAttributes.get("columnSpecs") != null
488                    ? containerAttributes.get("columnSpecs")
489                    : "";
490            String rowSpecs = containerAttributes.get("rowSpecs") != null
491                    ? containerAttributes.get("rowSpecs")
492                    : "";
493
494            final ContainerLayout containerLayout = new ContainerLayout(
495                    containerName, columnSpecs, rowSpecs);
496
497            Node[] cellConstraints = getNodesNamed(containerNode,
498                    "cellconstraints");
499            for (Node cellConstraint : cellConstraints) {
500                Map<String, String> constraintAttributes = getAttributeMap(
501                        cellConstraint);
502
503                String name = null;
504                CellConstraints.Alignment horizontalAlignment = CellConstraints.DEFAULT;
505                CellConstraints.Alignment verticalAlignment = CellConstraints.DEFAULT;
506                int gridX = 1;
507                int gridY = 1;
508                int gridWidth = 1;
509                int gridHeight = 1;
510                int topInset = 0;
511                int bottomInset = 0;
512                int rightInset = 0;
513                int leftInset = 0;
514
515                if (constraintAttributes.get("name") == null) {
516                    throw new RuntimeException(
517                            "cellconstraints attribute name cannot be null for container "
518                                    + containerName);
519                }
520                name = constraintAttributes.get("name");
521                if (constraintAttributes.get("horizontalAlignment") != null) {
522                    horizontalAlignment = getAlignment(
523                            constraintAttributes.get("horizontalAlignment"));
524                }
525                if (constraintAttributes.get("verticalAlignment") != null) {
526                    verticalAlignment = getAlignment(
527                            constraintAttributes.get("verticalAlignment"));
528                }
529                if (constraintAttributes.get("gridX") != null) {
530                    gridX = Integer.parseInt(constraintAttributes.get("gridX"));
531                }
532                if (constraintAttributes.get("gridY") != null) {
533                    gridY = Integer.parseInt(constraintAttributes.get("gridY"));
534                }
535                if (constraintAttributes.get("gridWidth") != null) {
536                    gridWidth = Integer
537                            .parseInt(constraintAttributes.get("gridWidth"));
538                }
539                if (constraintAttributes.get("gridHeight") != null) {
540                    gridHeight = Integer
541                            .parseInt(constraintAttributes.get("gridHeight"));
542                }
543                if (constraintAttributes.get("topInset") != null) {
544                    topInset = Integer
545                            .parseInt(constraintAttributes.get("topInset"));
546                }
547                if (constraintAttributes.get("bottomInset") != null) {
548                    bottomInset = Integer
549                            .parseInt(constraintAttributes.get("bottomInset"));
550                }
551                if (constraintAttributes.get("rightInset") != null) {
552                    rightInset = Integer
553                            .parseInt(constraintAttributes.get("rightInset"));
554                }
555                if (constraintAttributes.get("leftInset") != null) {
556                    leftInset = Integer
557                            .parseInt(constraintAttributes.get("leftInset"));
558                }
559
560                CellConstraints constraints = new CellConstraints(gridX, gridY,
561                        gridWidth, gridHeight, horizontalAlignment,
562                        verticalAlignment, new Insets(topInset, leftInset,
563                                bottomInset, rightInset));
564
565                containerLayout.addCellConstraints(name, constraints);
566            }
567
568            Node[] propertiesNodes = getNodesNamed(containerNode, "properties");
569
570            // this is sooooo lame. we now how to construct a fake xml doc
571            // so the parser can read it. i'm starting to think it would have
572            // been easier to just do the whole damn thing by hand. arggg..
573            String fakeDoc = "<java version=\"1.4.0\" class=\"java.beans.XMLDecoder\">";
574            fakeDoc += "<void id=\"controller\" property=\"owner\"/>\n";
575            fakeDoc += "<object idref=\"controller\">";
576
577            for (Node propertiesNode : propertiesNodes) {
578                Map<String, String> propertyAttributes = getAttributeMap(
579                        propertiesNode);
580                String componentName = propertyAttributes.get("component");
581                if (componentName == null) {
582                    throw new RuntimeException(
583                            "propertyset must have an attribute called component");
584                }
585
586                Node[] propertyNodes = getNodesNamed(propertiesNode,
587                        "property");
588                for (Node propertyNode : propertyNodes) {
589                    Map<String, String> voidAttributes = getAttributeMap(
590                            propertyNode);
591                    String property = voidAttributes.get("name");
592                    if (property == null) {
593                        throw new RuntimeException(
594                                "property element must have a name");
595                    }
596                    fakeDoc += "<void method=\"setProperty\"><string>"
597                            + componentName + "</string>";
598                    fakeDoc += "<string>" + property + "</string>";
599                    fakeDoc += createString(propertyNode.getChildNodes());
600                    fakeDoc += "</void>\n";
601
602                }
603            }
604
605            fakeDoc += "</object></java>";
606
607            if (propertiesNodes.length > 0) {
608
609                //        Object controller = new Object()
610                //        {
611                //          public void configureProperty(String componentName, String property,
612                //              Object value)
613                //          {
614                //            containerLayout.setProperty(componentName, property, value);
615                //          }
616                //        };
617
618                XMLDecoder decoder = new XMLDecoder(
619                        new ByteArrayInputStream(fakeDoc.getBytes()));
620                decoder.setOwner(containerLayout);
621                decoder.readObject();
622                decoder.close();
623            }
624
625            layoutConstraintsManager.addLayout(containerLayout);
626        }
627
628        return layoutConstraintsManager;
629    }
630
631    private static Map<String, String> getAttributeMap(Node node) {
632
633        Map<String, String> attributeMap = new HashMap<String, String>();
634
635        NamedNodeMap attributes = node.getAttributes();
636        if (attributes != null) {
637            for (int index = 0; index < attributes.getLength(); index++) {
638                Node attribute = attributes.item(index);
639                attributeMap.put(attribute.getNodeName(),
640                        attribute.getNodeValue());
641            }
642        }
643
644        return attributeMap;
645    }
646
647    private static Node[] getNodesNamed(Node parent, String nodeName) {
648        NodeList children = parent.getChildNodes();
649        List<Node> childList = new ArrayList<Node>();
650        for (int i = 0; i < children.getLength(); i++) {
651            if (nodeName.equals(children.item(i).getNodeName())) {
652                childList.add(children.item(i));
653            }
654        }
655        Node[] result = new Node[childList.size()];
656        return childList.toArray(result);
657    }
658
659    public static void main(String[] args) {
660        LayoutConstraintsManager l = LayoutConstraintsManager
661                .getLayoutConstraintsManager(LayoutConstraintsManager.class
662                        .getResourceAsStream("editableLayoutConstraints.xml"));
663        /*ContainerLayout cl =*/l.getContainerLayout("mainLayout");
664    }
665
666}