001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2014  Oliver Burn
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019package com.puppycrawl.tools.checkstyle.checks.indentation;
020
021import com.google.common.collect.Maps;
022import com.puppycrawl.tools.checkstyle.api.DetailAST;
023import com.puppycrawl.tools.checkstyle.api.TokenTypes;
024import java.lang.reflect.Constructor;
025import java.lang.reflect.InvocationTargetException;
026import java.util.Map;
027import java.util.Set;
028import org.apache.commons.logging.Log;
029import org.apache.commons.logging.LogFactory;
030
031/**
032 * Factory for handlers. Looks up constructor via reflection.
033 *
034 * @author jrichard
035 */
036public class HandlerFactory
037{
038    /** Logger for indentation check */
039    private static final Log LOG =
040        LogFactory.getLog("com.puppycrawl.tools.checkstyle.checks.indentation");
041
042    /**
043     * Registered handlers.
044     */
045    private final Map<Integer, Constructor<?>> typeHandlers =
046        Maps.newHashMap();
047
048    /**
049     * registers a handler
050     *
051     * @param type
052     *                type from TokenTypes
053     * @param handlerClass
054     *                the handler to register
055     */
056    private void register(int type, Class<?> handlerClass)
057    {
058        try {
059            final Constructor<?> ctor = handlerClass
060                    .getConstructor(new Class[] {IndentationCheck.class,
061                        DetailAST.class, // current AST
062                        ExpressionHandler.class, // parent
063                    });
064            typeHandlers.put(type, ctor);
065        }
066        ///CLOVER:OFF
067        catch (final NoSuchMethodException e) {
068            throw new RuntimeException("couldn't find ctor for "
069                                       + handlerClass);
070        }
071        catch (final SecurityException e) {
072            LOG.debug("couldn't find ctor for " + handlerClass, e);
073            throw new RuntimeException("couldn't find ctor for "
074                                       + handlerClass);
075        }
076        ///CLOVER:ON
077    }
078
079    /** Creates a HandlerFactory. */
080    public HandlerFactory()
081    {
082        register(TokenTypes.CASE_GROUP, CaseHandler.class);
083        register(TokenTypes.LITERAL_SWITCH, SwitchHandler.class);
084        register(TokenTypes.SLIST, SlistHandler.class);
085        register(TokenTypes.PACKAGE_DEF, PackageDefHandler.class);
086        register(TokenTypes.LITERAL_ELSE, ElseHandler.class);
087        register(TokenTypes.LITERAL_IF, IfHandler.class);
088        register(TokenTypes.LITERAL_TRY, TryHandler.class);
089        register(TokenTypes.LITERAL_CATCH, CatchHandler.class);
090        register(TokenTypes.LITERAL_FINALLY, FinallyHandler.class);
091        register(TokenTypes.LITERAL_DO, DoWhileHandler.class);
092        register(TokenTypes.LITERAL_WHILE, WhileHandler.class);
093        register(TokenTypes.LITERAL_FOR, ForHandler.class);
094        register(TokenTypes.METHOD_DEF, MethodDefHandler.class);
095        register(TokenTypes.CTOR_DEF, MethodDefHandler.class);
096        register(TokenTypes.CLASS_DEF, ClassDefHandler.class);
097        register(TokenTypes.ENUM_DEF, ClassDefHandler.class);
098        register(TokenTypes.OBJBLOCK, ObjectBlockHandler.class);
099        register(TokenTypes.INTERFACE_DEF, ClassDefHandler.class);
100        register(TokenTypes.IMPORT, ImportHandler.class);
101        register(TokenTypes.ARRAY_INIT, ArrayInitHandler.class);
102        register(TokenTypes.METHOD_CALL, MethodCallHandler.class);
103        register(TokenTypes.CTOR_CALL, MethodCallHandler.class);
104        register(TokenTypes.LABELED_STAT, LabelHandler.class);
105        register(TokenTypes.STATIC_INIT, StaticInitHandler.class);
106        register(TokenTypes.INSTANCE_INIT, SlistHandler.class);
107        register(TokenTypes.VARIABLE_DEF, MemberDefHandler.class);
108        register(TokenTypes.LITERAL_NEW, NewHandler.class);
109        register(TokenTypes.INDEX_OP, IndexHandler.class);
110    }
111
112    /**
113     * Returns true if this type (form TokenTypes) is handled.
114     *
115     * @param type type from TokenTypes
116     * @return true if handler is registered, false otherwise
117     */
118    public boolean isHandledType(int type)
119    {
120        final Set<Integer> typeSet = typeHandlers.keySet();
121        return typeSet.contains(type);
122    }
123
124    /**
125     * Gets list of registered handler types.
126     *
127     * @return int[] of TokenType types
128     */
129    public int[] getHandledTypes()
130    {
131        final Set<Integer> typeSet = typeHandlers.keySet();
132        final int[] types = new int[typeSet.size()];
133        int index = 0;
134        for (final Integer val : typeSet) {
135            types[index++] = val;
136        }
137
138        return types;
139    }
140
141    /**
142     * Get the handler for an AST.
143     *
144     * @param indentCheck   the indentation check
145     * @param ast           ast to handle
146     * @param parent        the handler parent of this AST
147     *
148     * @return the ExpressionHandler for ast
149     */
150    public ExpressionHandler getHandler(IndentationCheck indentCheck,
151        DetailAST ast, ExpressionHandler parent)
152    {
153        final ExpressionHandler handler =
154            createdHandlers.get(ast);
155        if (handler != null) {
156            return handler;
157        }
158
159        if (ast.getType() == TokenTypes.METHOD_CALL) {
160            return createMethodCallHandler(indentCheck, ast, parent);
161        }
162
163        ExpressionHandler expHandler = null;
164        try {
165            final Constructor<?> handlerCtor =
166                typeHandlers.get(ast.getType());
167            if (handlerCtor != null) {
168                expHandler = (ExpressionHandler) handlerCtor.newInstance(
169                        indentCheck, ast, parent);
170            }
171        }
172        ///CLOVER:OFF
173        catch (final InstantiationException e) {
174            LOG.debug("couldn't instantiate constructor for " + ast, e);
175            throw new RuntimeException("couldn't instantiate constructor for "
176                                       + ast);
177        }
178        catch (final IllegalAccessException e) {
179            LOG.debug("couldn't access constructor for " + ast, e);
180            throw new RuntimeException("couldn't access constructor for "
181                                       + ast);
182        }
183        catch (final InvocationTargetException e) {
184            LOG.debug("couldn't instantiate constructor for " + ast, e);
185            throw new RuntimeException("couldn't instantiate constructor for "
186                                       + ast);
187        }
188        if (expHandler == null) {
189            throw new RuntimeException("no handler for type " + ast.getType());
190        }
191        ///CLOVER:ON
192        return expHandler;
193    }
194
195    /**
196     * Create new instance of handler for METHOD_CALL.
197     *
198     * @param indentCheck   the indentation check
199     * @param ast           ast to handle
200     * @param parent        the handler parent of this AST
201     *
202     * @return new instance.
203     */
204    ExpressionHandler createMethodCallHandler(IndentationCheck indentCheck,
205        DetailAST ast, ExpressionHandler parent)
206    {
207        ExpressionHandler theParent = parent;
208        DetailAST astNode = ast.getFirstChild();
209        while ((astNode != null) && (astNode.getType() == TokenTypes.DOT)) {
210            astNode = astNode.getFirstChild();
211        }
212        if ((astNode != null) && isHandledType(astNode.getType())) {
213            theParent = getHandler(indentCheck, astNode, theParent);
214            createdHandlers.put(astNode, theParent);
215        }
216        return new MethodCallHandler(indentCheck, ast, theParent);
217    }
218
219    /** Clears cache of created handlers. */
220    void clearCreatedHandlers()
221    {
222        createdHandlers.clear();
223    }
224
225    /** cache for created method call handlers */
226    private final Map<DetailAST, ExpressionHandler> createdHandlers =
227        Maps.newHashMap();
228}