All Downloads are FREE. Search and download functionalities are using the official Maven repository.

groovy.transform.stc.MapEntryOrKeyValue Maven / Gradle / Ivy

The newest version!
/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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;

/**
 * 

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.

*

The result is a closure which can have the following forms:

*
    *
  • { key, value -> ...} where key is the key of a map entry, and value the corresponding value
  • *
  • { entry -> ... } where entry is a {@link java.util.Map.Entry} map entry
  • *
  • { ...} where it is an implicit {@link java.util.Map.Entry} map entry
  • *
*

This hint handles all those cases by picking the generics from the first argument of the method (by default).

*

The options array is used to modify the behavior of this hint. Each string in the option array consists of * a key=value pair.

*
    *
  • argNum=index of the parameter representing the map (by default, 0)
  • *
  • index=true or false, by default false. If true, then an additional "int" parameter is added, * for "withIndex" variants
  • *
* void doSomething(String str, Map<K,>V map, @ClosureParams(value=MapEntryOrKeyValue.class,options="argNum=1") Closure c) { ... } */ public class MapEntryOrKeyValue extends ClosureSignatureHint { private static final ClassNode MAPENTRY_TYPE = ClassHelper.make(Map.Entry.class); public List 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.parseInt(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= or index= ", source.getLineNumber(), source.getColumnNumber()); } } return new Options(pIndex, generateIndex); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy