001    /*
002     * Created on Dec 22, 2007
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
005     * the License. You may obtain a copy of the License at
006     * 
007     * http://www.apache.org/licenses/LICENSE-2.0
008     * 
009     * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
010     * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
011     * specific language governing permissions and limitations under the License.
012     * 
013     * Copyright @2007-2010 the original author or authors.
014     */
015    package org.fest.swing.core;
016    
017    import static org.fest.swing.edt.GuiActionRunner.execute;
018    import static org.fest.swing.format.Formatting.format;
019    import static org.fest.swing.hierarchy.NewHierarchy.ignoreExistingComponents;
020    
021    import java.awt.Component;
022    import java.awt.Container;
023    import java.io.PrintStream;
024    
025    import org.fest.swing.annotation.RunsInCurrentThread;
026    import org.fest.swing.annotation.RunsInEDT;
027    import org.fest.swing.edt.GuiTask;
028    import org.fest.swing.hierarchy.*;
029    
030    /**
031     * Understands printing the <code>String</code> representation of <code>{@link java.awt.Component}</code>s to
032     * facilitate debugging.
033     * 
034     * @author Alex Ruiz
035     */
036    public final class BasicComponentPrinter implements ComponentPrinter {
037    
038      private static final String INDENTATION = "  ";
039      
040      private static final ComponentMatcher ALWAYS_MATCHES = alwaysMatches();
041      
042      private static ComponentMatcher alwaysMatches() {
043        return new ComponentMatcher() {
044          public boolean matches(Component c) {
045            return true;
046          }
047        };
048      }
049      
050      private final ComponentHierarchy hierarchy;
051    
052      /**
053       * Creates a new <code>{@link BasicComponentPrinter}</code> with a new AWT hierarchy. <code>{@link Component}</code>s
054       * created before the created <code>{@link BasicComponentPrinter}</code> cannot be accessed by the created
055       * <code>{@link BasicComponentPrinter}</code>.
056       * @return the created finder.
057       */
058      public static ComponentPrinter printerWithNewAwtHierarchy() {
059        return new BasicComponentPrinter(ignoreExistingComponents());
060      }
061    
062      /**
063       * Creates a new <code>{@link BasicComponentPrinter}</code> that has access to all the GUI components in the AWT
064       * hierarchy.
065       * @return the created printer.
066       */
067      public static ComponentPrinter printerWithCurrentAwtHierarchy() {
068        return new BasicComponentPrinter(new ExistingHierarchy());
069      }
070    
071      /**
072       * Creates a new <code>{@link BasicComponentPrinter}</code>.
073       * @param hierarchy the component hierarchy to use.
074       */
075      protected BasicComponentPrinter(ComponentHierarchy hierarchy) {
076        this.hierarchy = hierarchy;
077      }
078      
079      /**
080       * Returns the component hierarchy used by this printer.
081       * @return the component hierarchy used by this printer.
082       */
083      protected final ComponentHierarchy hierarchy() { return hierarchy; }
084      
085      /** {@inheritDoc} */
086      @RunsInEDT
087      public void printComponents(PrintStream out) {
088        printComponents(out, ALWAYS_MATCHES);
089      }
090    
091      /** {@inheritDoc} */
092      @RunsInEDT
093      public void printComponents(PrintStream out, Container root) {
094        printComponents(out, ALWAYS_MATCHES, root);
095      }
096    
097      /** {@inheritDoc} */
098      @RunsInEDT
099      public void printComponents(PrintStream out, Class<? extends Component> type) {
100        printComponents(out, type, null);
101      }
102      
103      /** {@inheritDoc} */
104      @RunsInEDT
105      public void printComponents(PrintStream out, Class<? extends Component> type, Container root) {
106        validateNotNull(out);
107        if (type == null) throw new NullPointerException("The type to match should not be null");
108        print(hierarchy(root), new TypeMatcher(type), out);
109      }
110    
111      /** ${@inheritDoc} */
112      public void printComponents(PrintStream out, ComponentMatcher matcher) {
113        printComponents(out, matcher, null);
114      }
115    
116      /** ${@inheritDoc} */
117      public void printComponents(PrintStream out, ComponentMatcher matcher, Container root) {
118        validateNotNull(out);
119        if (matcher == null) throw new NullPointerException("The matcher to use as filter should not be null");
120        print(hierarchy(root), matcher, out);
121      }
122    
123      private void validateNotNull(PrintStream out) {
124        if (out == null) throw new NullPointerException("The output stream should not be null");
125      }
126      
127      private ComponentHierarchy hierarchy(Container root) {
128        return root != null ? new SingleComponentHierarchy(root, hierarchy) : hierarchy;
129      }
130      
131      @RunsInEDT
132      private static void print(final ComponentHierarchy hierarchy, final ComponentMatcher matcher, final PrintStream out) {
133        execute(new GuiTask() {
134          protected void executeInEDT() {
135            for (Component c : hierarchy.roots()) print(c, hierarchy, matcher, 0, out);
136          }
137        });
138      }
139      
140      @RunsInCurrentThread
141      private static void print(Component c, ComponentHierarchy h, ComponentMatcher matcher, int level,
142          PrintStream out) {
143        if (matcher.matches(c)) print(c, level, out);
144        for (Component child : h.childrenOf(c))
145          print(child, h, matcher, level + 1, out);
146      }
147    
148      @RunsInCurrentThread
149      private static void print(Component c, int level, PrintStream out) {
150        for (int i = 0; i < level; i++) out.print(INDENTATION);
151        out.println(format(c));
152      }
153    }