Package groovy.transform.stc

Source Code of groovy.transform.stc.MapEntryOrKeyValue

/*
* Copyright 2003-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package groovy.transform.stc;

import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.GenericsType;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.SourceUnit;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
* <p>A special hint which handles a common use case in the Groovy methods that work on maps. In case of an
* iteration on a list of map entries, you often want the user to be able to work either on a {@link java.util.Map.Entry} map entry
* or on a key,value pair.</p>
* <p>The result is a closure which can have the following forms:</p>
* <ul>
*     <li><code>{ key, value -> ...}</code> where key is the key of a map entry, and value the corresponding value</li>
*     <li><code>{ entry -> ... }</code> where entry is a {@link java.util.Map.Entry} map entry</li>
*     <li><code>{ ...}</code> where <i>it</i> is an implicit {@link java.util.Map.Entry} map entry</li>
* </ul>
* <p>This hint handles all those cases by picking the generics from the first argument of the method (by default).</p>
* <p>The options array is used to modify the behavior of this hint. Each string in the option array consists of
* a key=value pair.</p>
* <ul>
*     <li><i>argNum=index</i> of the parameter representing the map (by default, 0)</li>
*     <li><i>index=true or false</i>, by default false. If true, then an additional "int" parameter is added,
*     for "withIndex" variants</li>
* </ul>
* <code>void doSomething(String str, Map&lt;K,&gt;V map, @ClosureParams(value=MapEntryOrKeyValue.class,options="1") Closure c) { ... }</code>
*/
public class MapEntryOrKeyValue extends ClosureSignatureHint {
    private static final ClassNode MAPENTRY_TYPE = ClassHelper.make(Map.Entry.class);

    public List<ClassNode[]> getClosureSignatures(final MethodNode node, final SourceUnit sourceUnit, final CompilationUnit compilationUnit, final String[] options, final ASTNode usage) {
        Options opt;
        try {
            opt = Options.parse(node, usage, options);
        } catch (IncorrectTypeHintException e) {
            sourceUnit.addError(e);
            return Collections.emptyList();
        }
        GenericsType[] genericsTypes = node.getParameters()[opt.parameterIndex].getOriginType().getGenericsTypes();
        if (genericsTypes==null) {
            // would happen if you have a raw Map type for example
            genericsTypes = new GenericsType[] {
                new GenericsType(ClassHelper.OBJECT_TYPE),
                new GenericsType(ClassHelper.OBJECT_TYPE)
            };
        }
        ClassNode[] firstSig;
        ClassNode[] secondSig;
        ClassNode mapEntry = MAPENTRY_TYPE.getPlainNodeReference();
        mapEntry.setGenericsTypes(genericsTypes);
        if (opt.generateIndex) {
            firstSig = new ClassNode[] {genericsTypes[0].getType(), genericsTypes[1].getType(), ClassHelper.int_TYPE};
            secondSig = new ClassNode[] {mapEntry, ClassHelper.int_TYPE};

        } else {
            firstSig = new ClassNode[] {genericsTypes[0].getType(), genericsTypes[1].getType()};
            secondSig = new ClassNode[] {mapEntry};
        }
        return Arrays.asList(firstSig, secondSig);
    }
   
    private static final class Options {
        final int parameterIndex;
        final boolean generateIndex;

        private Options(final int parameterIndex, final boolean generateIndex) {
            this.parameterIndex = parameterIndex;
            this.generateIndex = generateIndex;
        }
       
        static Options parse(MethodNode mn, ASTNode source, String[] options) throws IncorrectTypeHintException {
            int pIndex = 0;
            boolean generateIndex = false;
            for (String option : options) {
                String[] keyValue = option.split("=");
                if (keyValue.length==2) {
                    String key = keyValue[0];
                    String value = keyValue[1];
                    if ("argNum".equals(key)) {
                        pIndex = Integer.valueOf(value);
                    } else if ("index".equals(key)) {
                        generateIndex = Boolean.valueOf(value);
                    } else {
                        throw new IncorrectTypeHintException(mn, "Unrecognized option: "+key, source.getLineNumber(), source.getColumnNumber());
                    }
                } else {
                    throw new IncorrectTypeHintException(mn, "Incorrect option format. Should be argNum=<num> or index=<boolean> ", source.getLineNumber(), source.getColumnNumber());
                }
            }
            return new Options(pIndex, generateIndex);
        }
    }
}
TOP

Related Classes of groovy.transform.stc.MapEntryOrKeyValue

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.