/******************************************************************************* * Copyright (c) 2008, 2017 xored software, Inc. and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * xored software, Inc. - initial API and Implementation (Andrei Sobolev) *******************************************************************************/ package org.eclipse.dltk.tcl.parser.definitions; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.eclipse.dltk.tcl.definitions.Argument; import org.eclipse.dltk.tcl.definitions.ArgumentType; import org.eclipse.dltk.tcl.definitions.Command; import org.eclipse.dltk.tcl.definitions.ComplexArgument; import org.eclipse.dltk.tcl.definitions.Constant; import org.eclipse.dltk.tcl.definitions.DefinitionsFactory; import org.eclipse.dltk.tcl.definitions.Group; import org.eclipse.dltk.tcl.definitions.Switch; import org.eclipse.dltk.tcl.definitions.TypedArgument; import org.eclipse.emf.common.util.EList; public class DefinitionUtils { /** * Generate switch variants. */ public final static String GENERATE_VARIANTS = "reduce_to_less"; public static final String SWITCH_COUNT = "switch_count"; private static boolean isSet(Map<String, Object> options, String key) { if (options == null) { return false; } if (!options.containsKey(key)) { return false; } Object object = options.get(key); if (object instanceof Boolean) { return ((Boolean) object).booleanValue(); } return false; } private static int getInt(Map<String, Object> options, String key, int def) { if (options == null) { return def; } if (!options.containsKey(key)) { return def; } Object object = options.get(key); if (object instanceof Integer) { return ((Integer) object).intValue(); } return def; } public static List<List<Argument>> reduceSwitches(List<Argument> list, Map<String, Object> options) { List<List<Argument>> argumentsList = new ArrayList<>(); for (Argument argument : list) { List<Argument> args = reduceSwitchesArgument(argument, options); List<List<Argument>> newArgumentsList = new ArrayList<>(); if (argumentsList.size() == 0) { for (Argument arg : args) { List<Argument> a = new ArrayList<>(); a.add(arg); newArgumentsList.add(a); } } else { for (Argument nextArgument : args) { // Add argument to all other. Fill existed and append for (List<Argument> al : argumentsList) { List<Argument> nal = new ArrayList<>(); for (Argument argument2 : al) { nal.add(copyArgument(argument2)); } nal.add(nextArgument); newArgumentsList.add(nal); } } } argumentsList = newArgumentsList; } return argumentsList; } public static Command[] reduceSwitches(Command command) { return reduceSwitches(command, null); } public static Command[] reduceSwitches(Command command, Map<String, Object> options) { List<Command> commands = new ArrayList<>(); EList<Argument> arguments = command.getArguments(); List<List<Argument>> removeSwitches = reduceSwitches(arguments, options); for (List<Argument> list : removeSwitches) { Command newCommand = DefinitionsFactory.eINSTANCE.createCommand(); newCommand.setDeprecated(command.getDeprecated()); newCommand.setName(command.getName()); newCommand.setVersion(command.getVersion()); newCommand.getArguments().addAll(list); commands.add(newCommand); } return commands.toArray(new Command[commands.size()]); } private static List<Argument> reduceSwitchesArgument(Argument argument, Map<String, Object> options) { List<Argument> results = new ArrayList<>(); if (argument instanceof Switch) { Switch sw = (Switch) argument; if (isSet(options, GENERATE_VARIANTS)) { if (sw.getUpperBound() == -1) { sw = (Switch) copyArgument(sw); sw.setUpperBound(sw.getLowerBound() + getInt(options, SWITCH_COUNT, 1)); } } if (sw.getUpperBound() != -1) { List<List<Group>> switchVariants = new ArrayList<>(); for (int i = 0; i < sw.getLowerBound(); i++) { List<Group> list = processGroups(sw, 1, 1, options); switchVariants = updateVariants(switchVariants, list, false); } for (int i = sw.getLowerBound(); i < sw.getUpperBound(); i++) { List<Group> list = processGroups(sw, 0, 1, options); switchVariants = updateVariants(switchVariants, list, isSet(options, GENERATE_VARIANTS)); } if (switchVariants.size() > 1) { for (List<Group> list : switchVariants) { addResult(results, list); } } else { List<Group> gg = switchVariants.get(0); if (gg.size() == 1) { results.add(gg.get(0)); } else { addResult(results, gg); } } } else { // Just copy switch. EList<Group> groups = sw.getGroups(); Switch nsw = copySwitch(sw); for (Group group : groups) { List<Argument> variants = reduceSwitchesArgument(group, options); for (Argument variant : variants) { nsw.getGroups().add((Group) variant); } } results.add(nsw); } } else if (argument instanceof Group) { Group g = (Group) argument; List<List<Argument>> removeSwitches = reduceSwitches( g.getArguments(), options); if (removeSwitches.size() > 0) { for (List<Argument> list : removeSwitches) { Group ng = copyGroup(g); ng.getArguments().addAll(list); results.add(ng); } } else { Group ng = copyGroup(g); results.add(ng); } } else if (argument instanceof ComplexArgument) { ComplexArgument c = (ComplexArgument) argument; List<List<Argument>> removeSwitches = reduceSwitches( c.getArguments(), options); if (removeSwitches.size() > 0) { for (List<Argument> list : removeSwitches) { ComplexArgument ng = copyComplexArgument(c); ng.getArguments().addAll(list); results.add(ng); } } else { ComplexArgument ng = copyComplexArgument(c); results.add(ng); } } else if (argument instanceof Constant) { Constant nc = copyConstant((Constant) argument); results.add(nc); } else if (argument instanceof TypedArgument) { TypedArgument tc = copyTypedArgument((TypedArgument) argument); results.add(tc); } return results; } public static Switch copySwitch(Switch sw) { Switch nsw = DefinitionsFactory.eINSTANCE.createSwitch(); nsw.setLowerBound(sw.getLowerBound()); nsw.setUpperBound(sw.getUpperBound()); nsw.setName(sw.getName()); return nsw; } public static TypedArgument copyTypedArgument(TypedArgument t) { TypedArgument nt = DefinitionsFactory.eINSTANCE.createTypedArgument(); nt.setLowerBound(t.getLowerBound()); nt.setUpperBound(t.getUpperBound()); nt.setType(t.getType()); nt.setName(t.getName()); return nt; } public static Constant copyConstant(Constant c) { Constant nc = DefinitionsFactory.eINSTANCE.createConstant(); nc.setLowerBound(c.getLowerBound()); nc.setUpperBound(c.getUpperBound()); nc.setName(c.getName()); return nc; } public static ComplexArgument copyComplexArgument(ComplexArgument c) { ComplexArgument nc = DefinitionsFactory.eINSTANCE .createComplexArgument(); nc.setLowerBound(c.getLowerBound()); nc.setUpperBound(c.getUpperBound()); nc.setName(c.getName()); return nc; } public static Group copyGroup(Group g) { Group ng = DefinitionsFactory.eINSTANCE.createGroup(); ng.setLowerBound(g.getLowerBound()); ng.setUpperBound(g.getUpperBound()); ng.setConstant(g.getConstant()); ng.setName(g.getName()); return ng; } public static void addResult(List<Argument> results, List<Group> list) { Group ng = DefinitionsFactory.eINSTANCE.createGroup(); ng.setLowerBound(1); ng.setUpperBound(1); ng.getArguments().addAll(list); results.add(ng); } private static List<List<Group>> updateVariants( List<List<Group>> switchVariants, List<Group> list, boolean addPrevious) { List<List<Group>> resultList = new ArrayList<>(); if (switchVariants.size() == 0) { for (Group g : list) { List<Group> gg = new ArrayList<>(); gg.add(g); resultList.add(gg); } } else { // We need to copy all of variants and add one element to each. for (Group g : list) { for (List<Group> nle : switchVariants) { Argument rg = copyArgument(g); List<Group> nl = new ArrayList<>(); for (Group nlee : nle) { Argument nleer = copyArgument(nlee); nl.add((Group) nleer); } nl.add((Group) rg); resultList.add(nl); } } } if (addPrevious) { resultList.addAll(switchVariants); } return resultList; } public static Argument copyArgument(Argument a) { if (a instanceof Constant) { return copyConstant((Constant) a); } else if (a instanceof TypedArgument) { return copyTypedArgument((TypedArgument) a); } else if (a instanceof Switch) { Switch s = (Switch) a; Switch ns = copySwitch(s); EList<Group> groups = s.getGroups(); for (Group group : groups) { ns.getGroups().add((Group) copyArgument(group)); } return ns; } else if (a instanceof Group) { Group g = (Group) a; Group ng = copyGroup(g); EList<Argument> arguments = g.getArguments(); for (Argument argument : arguments) { ng.getArguments().add(copyArgument(argument)); } return ng; } else if (a instanceof ComplexArgument) { ComplexArgument g = (ComplexArgument) a; ComplexArgument ng = copyComplexArgument(g); EList<Argument> arguments = g.getArguments(); for (Argument argument : arguments) { ng.getArguments().add(copyArgument(argument)); } return ng; } return null; } public static boolean equalsArgumentIgnoreName(Argument a1, Argument a2) { return equalsArgument(a1, a2, true); } public static boolean equalsArgument(Argument a1, Argument a2, boolean ignoreName) { if (a1.getClass() != a2.getClass()) return false; if (a1 == a2) return true; if (a1.getLowerBound() != a2.getLowerBound() || a1.getUpperBound() != a2.getUpperBound()) return false; if (!ignoreName && (a1.getName() != a2.getName() || a1.getName() == null || !a1.getName().equals(a2.getName()))) return false; if (a1 instanceof Constant) { String value1 = ((Constant) a1).getName(); String value2 = ((Constant) a2).getName(); if (value1 != value2 || value1 == null || !value1.equals(value2)) return false; } else if (a1 instanceof TypedArgument) { if (((TypedArgument) a1).getType() .getValue() != ((TypedArgument) a2).getType().getValue()) return false; } else if (a1 instanceof Switch) { if (((Switch) a1).getGroups().size() != ((Switch) a2).getGroups() .size()) return false; for (int i = 0; i < ((Switch) a1).getGroups().size(); i++) { if (!equalsArgument(((Switch) a1).getGroups().get(i), ((Switch) a2).getGroups().get(i), ignoreName)) return false; } } else if (a1 instanceof Group) { String const1 = ((Group) a1).getConstant(); String const2 = ((Group) a2).getConstant(); if (const1 != const2 && (const1 != null && !const1.equals(const2))) return false; if (((Group) a1).getArguments().size() != ((Group) a2) .getArguments().size()) return false; for (int i = 0; i < ((Group) a1).getArguments().size(); i++) { if (!equalsArgument(((Group) a1).getArguments().get(i), ((Group) a2).getArguments().get(i), ignoreName)) return false; } } else if (a1 instanceof ComplexArgument) { if (((ComplexArgument) a1).getArguments() .size() != ((ComplexArgument) a2).getArguments().size()) return false; for (int i = 0; i < ((ComplexArgument) a1).getArguments() .size(); i++) { if (!equalsArgument( ((ComplexArgument) a1).getArguments().get(i), ((ComplexArgument) a2).getArguments().get(i), ignoreName)) return false; } } return true; } public static Command copyCommand(Command c) { Command nc = DefinitionsFactory.eINSTANCE.createCommand(); nc.setName(c.getName()); nc.setDeprecated(c.getDeprecated()); nc.setVersion(c.getVersion()); for (Argument a : c.getArguments()) { nc.getArguments().add(copyArgument(a)); } return nc; } private static List<Group> processGroups(Switch sw, int l, int u, Map<String, Object> options) { EList<Group> groups = sw.getGroups(); List<Group> results = new ArrayList<>(); for (Group group : groups) { List<Argument> variants = reduceSwitchesArgument(group, options); for (Argument variant : variants) { Group ng = DefinitionsFactory.eINSTANCE.createGroup(); ng.setLowerBound(l); ng.setUpperBound(u); ng.getArguments().add(variant); results.add(ng); } } return results; } public static String convertToString(Command command) { return convertToString(command, false); } public static String convertToString(Command command, boolean clean) { StringBuffer buffer = new StringBuffer(); buffer.append(command.getName()); for (Argument arg : command.getArguments()) { buffer.append(' '); buffer.append(convertToString(arg, clean)); } return buffer.toString(); } private static String convertToString(Argument arg, boolean clean) { if (arg instanceof Constant) { return ((Constant) arg).getName() + boundsToString(arg); } else if (arg instanceof TypedArgument) { TypedArgument typed = (TypedArgument) arg; if (!clean) { return typed.getName() /* * + ":" + ((TypedArgument) arg).getType() */ + boundsToString(arg); } else { switch (typed.getType().getValue()) { case ArgumentType.SCRIPT_VALUE: return "{unk_cmd}"; case ArgumentType.EXPRESSION_VALUE: return "{$i>0}"; case ArgumentType.INDEX_VALUE: return "1"; case ArgumentType.NOT_NEGATIVE_VALUE: return "1"; case ArgumentType.INTEGER_VALUE: return "0"; case ArgumentType.BOOLEAN_VALUE: return "true"; case ArgumentType.VAR_NAME_VALUE: return "var"; case ArgumentType.LEVEL_VALUE: return "#1"; default:// Just a string return typed.getName(); } } } else if (arg instanceof Group) { Group group = (Group) arg; EList<Argument> arguments = group.getArguments(); StringBuffer b = new StringBuffer(); if (group.getArguments().size() > 0) { if (!clean) { b.append('('); } String constant = group.getConstant(); if (constant != null) { b.append(constant + " "); } boolean first = true; for (Argument argument : arguments) { if (!first) { b.append(" "); } else { first = false; } b.append(convertToString(argument, clean)); } if (!clean) { b.append(')'); } b.append(boundsToString(arg)); } else { String constant = group.getConstant(); if (constant != null) { b.append(constant); } } return b.toString(); } else if (arg instanceof ComplexArgument) { EList<Argument> arguments = ((ComplexArgument) arg).getArguments(); StringBuffer b = new StringBuffer(); if (!clean) { b.append('^'); } else { b.append('{'); } boolean first = true; for (Argument argument : arguments) { if (!first) { b.append(" "); } else { first = false; } b.append(convertToString(argument, clean)); } if (!clean) { b.append('^'); } else { b.append('}'); } b.append(boundsToString(arg)); return b.toString(); } else if (arg instanceof Switch) { if (clean) { throw new IllegalArgumentException(); } EList<Group> arguments = ((Switch) arg).getGroups(); StringBuffer b = new StringBuffer(); b.append("{"); boolean first = true; for (Argument argument : arguments) { if (!first) { b.append("|"); } else { first = false; } b.append(convertToString(argument, clean)); } b.append("}"); b.append(boundsToString(arg)); return b.toString(); } return arg.toString(); } private static String boundsToString(Argument arg) { return "";// "[" + arg.getLowerBound() + "," + arg.getUpperBound() + // "]"; } public static Constant extractGroupPseudoConstant(Group group) { String cval = group.getConstant(); if (cval != null && cval.length() > 0) { Constant constant = DefinitionsFactory.eINSTANCE.createConstant(); constant.setName(cval); constant.setLowerBound(1); constant.setUpperBound(1); constant.setStrictMatch(true); return constant; } return null; } public static Argument minimizeBounds(Argument argument) { Argument minArgument = copyArgument(argument); minArgument.setLowerBound(1); minArgument.setUpperBound(1); return minArgument; } public static List<Argument> minimizeBounds(List<Argument> arguments) { List<Argument> minArguments = new ArrayList<>(); for (Argument argument : arguments) { minArguments.add(minimizeBounds(argument)); } return minArguments; } public static boolean isOptions(Switch sw) { return sw.getLowerBound() == 0 && (sw.getUpperBound() == -1 || sw.getUpperBound() == sw.getGroups().size()); } public static boolean isMode(Switch sw) { for (Group group : sw.getGroups()) { if (group.getArguments().size() != 0) return false; } return true; } }