/** * Aptana Studio * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). * Please see the license.html included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package com.aptana.editor.php.internal.indexer; import gnu.trove.set.hash.THashSet; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import com.aptana.editor.php.indexer.IPHPIndexConstants; /** * PHP entry value for lambda functions. * * @author Shalom Gibly <sgibly@aptana.com> */ public class LambdaFunctionPHPEntryValue extends AbstractPHPEntryValue implements IPHPFunctionEntryValue { private static final String[] NO_STRING_PARAMS = new String[0]; private static final boolean[] NO_BOOLEAN_PARAMS = new boolean[0]; private static final Object[] NO_OBJECT_PARAMS = new Object[0]; private static final int[] NO_INT_PARAMS = new int[0]; /** * Parameters names. */ private String[] parameterNames; /** * Parameter types. */ private Object[] parameterTypes; private int[] parameterStartPositions; /** * Indicates whether the parameter is a mandatory one. */ private boolean[] parameterMandatories; /** * Function return types. Might be string value in case of direct type value or reference that might be used to * count the type indirectly. */ private Object returnTypes; /** * LambdaFunctionPHPEntryValue constructor. * * @param modifiers * - modifiers. * @param startPosition * - declaration start position. * @param nameSpace */ public LambdaFunctionPHPEntryValue(int modifiers, int startPosition, String nameSpace) { super(modifiers, nameSpace); this.setStartOffset(startPosition); } /** * LambdaFunctionPHPEntryValue constructor. * * @param modifiers * - modifiers. * @param parameters * - function parameters. * @param parameterStartPositions * - start position of each parameter. * @param parameterMandatories * - boolean array in the size of the parameters that indicates which of the params is a mandatory one. * @param startPosition * - declaration start position. * @param nameSpace * @throws IllegalArgumentException * in any case where the parameterMandatories parameter length is different from the expected. */ public LambdaFunctionPHPEntryValue(int modifiers, Map<String, Set<Object>> parameters, int[] parameterStartPositions, boolean[] parameterMandatories, int startPosition, String nameSpace) { super(modifiers, nameSpace); if (parameters != null) { if (parameters.size() != 0) { if (parameterStartPositions == null || parameters.size() != parameterStartPositions.length) { throw new IllegalArgumentException("Illegal parameter start positions: " + parameterStartPositions); //$NON-NLS-1$ } } this.parameterStartPositions = parameterStartPositions; if (parameterMandatories == null || parameters.size() != parameterMandatories.length) { // IdeLog.log(PHPPlugin.getDefault(), IStatus.WARNING, // "Optionals array length is not as expected. Expected " + parameters.size() + " and got " // + ((parameterMandatories == null) ? "null" : parameterMandatories.length), null); // try to recover from that if (parameterMandatories == null) { parameterMandatories = new boolean[parameters.size()]; } else if (parameters.size() > parameterMandatories.length) { boolean[] newMandatories = new boolean[parameters.size()]; System.arraycopy(parameterMandatories, 0, newMandatories, 0, parameterMandatories.length); parameterMandatories = newMandatories; } else // parameters.size() < parameterMandatories.length { // reduce the parameters mandatories size, as this is less important boolean[] newMandatories = new boolean[parameters.size()]; System.arraycopy(parameterMandatories, 0, newMandatories, 0, newMandatories.length); parameterMandatories = newMandatories; } } this.parameterMandatories = parameterMandatories; parameterNames = new String[parameters.size()]; parameterTypes = new Object[parameters.size()]; int i = 0; for (Entry<String, Set<Object>> entry : parameters.entrySet()) { parameterNames[i] = entry.getKey(); Set<Object> types = entry.getValue(); if (types != null && types.size() != 0) { if (types.size() == 1) { Object type = types.iterator().next(); parameterTypes[i] = type; } else { Set<Object> typesToSave = new THashSet<Object>(types.size()); typesToSave.addAll(types); parameterTypes[i] = typesToSave; } } i++; } } this.setStartOffset(startPosition); } /** * Constructs LambdaFunctionPHPEntryValue by loading it from an input steam. * * @param di * {@link DataInputStream} * @throws IOException */ public LambdaFunctionPHPEntryValue(DataInputStream di) throws IOException { super(di); internalRead(di); } /** * Sets function return type. * * @param type * - might be string value in case of direct type value or reference that might be used to count the type * indirectly. */ public void setReturnType(Object type) { returnTypes = type; } /** * Sets function return types. * * @param type * - might be string value in case of direct type value or reference that might be used to count the type * indirectly. */ public void setReturnTypes(Set<Object> types) { if (types != null && types.size() != 0) { if (types.size() == 1) { setReturnType(types.iterator().next()); } else { Object[] typesArray = new Object[types.size()]; types.toArray(typesArray); this.returnTypes = typesArray; } } } /** * Gets return type. * * @return string value in case of direct type value or reference that might be used to count the type indirectly. * null means unknown type. */ public Set<Object> getReturnTypes() { if (returnTypes == null) { return Collections.emptySet(); } if (returnTypes instanceof Object[]) { Object[] returnTypesArray = (Object[]) returnTypes; Set<Object> result = new THashSet<Object>(returnTypesArray.length); for (int i = 0; i < returnTypesArray.length; i++) { result.add(returnTypesArray[i]); } return result; } else { Set<Object> result = new THashSet<Object>(1); result.add(returnTypes); return result; } } /** * Returns an array of boolean describing which of the parameters is mandatory and which is optional. * * @return an array of boolean describing which of the parameters is mandatory and which is optional; Returns an * empty array in case there are no parameters. */ public boolean[] getMandatoryParams() { if (parameterMandatories == null) { return NO_BOOLEAN_PARAMS; } boolean[] toReturn = new boolean[parameterMandatories.length]; System.arraycopy(parameterMandatories, 0, toReturn, 0, parameterMandatories.length); return toReturn; } /** * Gets function parameters. * * @return function parameters. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public Map<String, Set<Object>> getParameters() { if (parameterNames != null) { Map<String, Set<Object>> result = new LinkedHashMap<String, Set<Object>>(parameterNames.length); for (int i = 0; i < parameterNames.length; i++) { Set<Object> types = new HashSet<Object>(); Object typeObj = parameterTypes[i]; if (typeObj != null) { if (typeObj instanceof Set) { types.addAll((Set) typeObj); } else { types.add(typeObj); } } result.put(parameterNames[i], types); } return result; } return Collections.emptyMap(); } /** * {@inheritDoc} */ @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result; return result; } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (getClass() != obj.getClass()) return false; if (!super.equals(obj)) return false; return true; } @Override public int getKind() { return IPHPIndexConstants.LAMBDA_FUNCTION_CATEGORY; } /** * Gets parameter start positions. * * @return parameter start positions. */ public int[] getParameterStartPositions() { return parameterStartPositions; } @Override protected void internalWrite(DataOutputStream da) throws IOException { IndexPersistence.writeType(returnTypes, da); int len = (parameterNames == null) ? 0 : parameterNames.length; da.writeInt(len); for (int a = 0; a < len; a++) { da.writeUTF(parameterNames[a]); da.writeBoolean(parameterMandatories[a]); da.writeInt(parameterStartPositions[a]); IndexPersistence.writeType(parameterTypes[a], da); } } @Override protected void internalRead(DataInputStream di) throws IOException { returnTypes = IndexPersistence.readType(di); int pc = di.readInt(); if (pc > 0) { parameterNames = new String[pc]; parameterMandatories = new boolean[pc]; parameterTypes = new Object[pc]; parameterStartPositions = new int[pc]; for (int a = 0; a < pc; a++) { parameterNames[a] = di.readUTF(); parameterMandatories[a] = di.readBoolean(); parameterStartPositions[a] = di.readInt(); parameterTypes[a] = IndexPersistence.readType(di); } } else { parameterNames = NO_STRING_PARAMS; parameterMandatories = NO_BOOLEAN_PARAMS; parameterStartPositions = NO_INT_PARAMS; parameterTypes = NO_OBJECT_PARAMS; } } }