public static void main(String[] args) { Map<String, String> map1 = Generics.newHashMap(); map1.put("a", "1"); map1.put("b", "2"); map1.put("c", "2"); map1.put("d", "4"); Map<String, String> map2 = Generics.newHashMap(); map2.put("1", "x"); map2.put("2", "y"); map2.put("3", "z"); System.out.println("map1: " + map1); System.out.println("invert(map1): " + Maps.invert(map1)); System.out.println("invertSet(map1): " + Maps.invertSet(map1)); System.out.println("map2: " + map2); System.out.println("compose(map1,map2): " + Maps.compose(map1, map2)); Map<String, Set<String>> setValues = Generics.newHashMap(); Map<String, List<String>> listValues = Generics.newHashMap(); Maps.putIntoValueArrayList(listValues, "a", "1"); Maps.putIntoValueArrayList(listValues, "a", "1"); Maps.putIntoValueArrayList(listValues, "a", "2"); Maps.putIntoValueHashSet(setValues, "a", "1"); Maps.putIntoValueHashSet(setValues, "a", "1"); Maps.putIntoValueHashSet(setValues, "a", "2"); System.out.println("listValues: " + listValues); System.out.println("setValues: " + setValues); }
@Test public void should_format_Map() { Map<String, Class<?>> map = new LinkedHashMap<String, Class<?>>(); map.put("One", String.class); map.put("Two", File.class); assertEquals("{'One'=java.lang.String, 'Two'=java.io.File}", Maps.format(map)); }
@NonNull public IAndroidTarget[] getMissingTargets() { synchronized (mLocalPackages) { if (mCachedMissingTargets == null) { Map<MissingTarget, MissingTarget> result = Maps.newHashMap(); Set<ISystemImage> seen = Sets.newHashSet(); for (IAndroidTarget target : getTargets()) { Collections.addAll(seen, target.getSystemImages()); } for (LocalPkgInfo local : getPkgsInfos(PkgType.PKG_ADDON_SYS_IMAGE)) { LocalAddonSysImgPkgInfo info = (LocalAddonSysImgPkgInfo) local; ISystemImage image = info.getSystemImage(); if (!seen.contains(image)) { addOrphanedSystemImage(image, info.getDesc(), result); } } for (LocalPkgInfo local : getPkgsInfos(PkgType.PKG_SYS_IMAGE)) { LocalSysImgPkgInfo info = (LocalSysImgPkgInfo) local; ISystemImage image = info.getSystemImage(); if (!seen.contains(image)) { addOrphanedSystemImage(image, info.getDesc(), result); } } mCachedMissingTargets = result.keySet(); } return mCachedMissingTargets.toArray(new IAndroidTarget[mCachedMissingTargets.size()]); } }
public NBTStorage(final File file, final String name) { this.root = (Map<String, Tag>) Maps.newHashMap(); this.file = file; if (!this.file.exists()) { this.create(); } this.name = name; }
private int repeatInternal( @NotNull PseudocodeImpl originalPseudocode, @Nullable Label startLabel, @Nullable Label finishLabel, int labelCount) { Integer startIndex = startLabel != null ? ((PseudocodeLabel) startLabel).getTargetInstructionIndex() : Integer.valueOf(0); assert startIndex != null; Integer finishIndex = finishLabel != null ? ((PseudocodeLabel) finishLabel).getTargetInstructionIndex() : Integer.valueOf(originalPseudocode.mutableInstructionList.size()); assert finishIndex != null; Map<Label, Label> originalToCopy = Maps.newLinkedHashMap(); Multimap<Instruction, Label> originalLabelsForInstruction = HashMultimap.create(); for (PseudocodeLabel label : originalPseudocode.labels) { Integer index = label.getTargetInstructionIndex(); if (index == null) continue; // label is not bounded yet if (label == startLabel || label == finishLabel) continue; if (startIndex <= index && index <= finishIndex) { originalToCopy.put(label, label.copy(labelCount++)); originalLabelsForInstruction.put(getJumpTarget(label), label); } } for (Label label : originalToCopy.values()) { labels.add((PseudocodeLabel) label); } for (int index = startIndex; index < finishIndex; index++) { Instruction originalInstruction = originalPseudocode.mutableInstructionList.get(index); repeatLabelsBindingForInstruction( originalInstruction, originalToCopy, originalLabelsForInstruction); Instruction copy = copyInstruction(originalInstruction, originalToCopy); addInstruction(copy); if (originalInstruction == originalPseudocode.errorInstruction && copy instanceof SubroutineExitInstruction) { errorInstruction = (SubroutineExitInstruction) copy; } if (originalInstruction == originalPseudocode.exitInstruction && copy instanceof SubroutineExitInstruction) { exitInstruction = (SubroutineExitInstruction) copy; } if (originalInstruction == originalPseudocode.sinkInstruction && copy instanceof SubroutineSinkInstruction) { sinkInstruction = (SubroutineSinkInstruction) copy; } } if (finishIndex < mutableInstructionList.size()) { repeatLabelsBindingForInstruction( originalPseudocode.mutableInstructionList.get(finishIndex), originalToCopy, originalLabelsForInstruction); } return labelCount; }
private void writeDelegateMethods( final ClassVisitor visitor, final Type generatedType, StructSchema<?> delegateSchema, Set<Class<?>> typesToDelegate) { Class<?> delegateClass = delegateSchema.getType().getConcreteClass(); Type delegateType = Type.getType(delegateClass); Map<Equivalence.Wrapper<Method>, Map<Class<?>, Method>> methodsToDelegate = Maps.newHashMap(); for (Class<?> typeToDelegate : typesToDelegate) { for (Method methodToDelegate : typeToDelegate.getMethods()) { if (ModelSchemaUtils.isIgnoredMethod(methodToDelegate)) { continue; } Equivalence.Wrapper<Method> methodKey = METHOD_EQUIVALENCE.wrap(methodToDelegate); Map<Class<?>, Method> methodsByReturnType = methodsToDelegate.get(methodKey); if (methodsByReturnType == null) { methodsByReturnType = Maps.newHashMap(); methodsToDelegate.put(methodKey, methodsByReturnType); } methodsByReturnType.put(methodToDelegate.getReturnType(), methodToDelegate); } } Set<Equivalence.Wrapper<Method>> delegateMethodKeys = ImmutableSet.copyOf( Iterables.transform( Arrays.asList(delegateClass.getMethods()), new Function<Method, Equivalence.Wrapper<Method>>() { @Override public Equivalence.Wrapper<Method> apply(Method method) { return METHOD_EQUIVALENCE.wrap(method); } })); for (Map.Entry<Equivalence.Wrapper<Method>, Map<Class<?>, Method>> entry : methodsToDelegate.entrySet()) { Equivalence.Wrapper<Method> methodKey = entry.getKey(); if (!delegateMethodKeys.contains(methodKey)) { continue; } Map<Class<?>, Method> methodsByReturnType = entry.getValue(); for (Method methodToDelegate : methodsByReturnType.values()) { writeDelegatedMethod(visitor, generatedType, delegateType, methodToDelegate); } } }
private static Map<ClassDescriptor, JetType> getSuperclassToSupertypeMap( ClassDescriptor containingClass) { Map<ClassDescriptor, JetType> superclassToSupertype = Maps.newHashMap(); for (JetType supertype : TypeUtils.getAllSupertypes(containingClass.getDefaultType())) { ClassifierDescriptor superclass = supertype.getConstructor().getDeclarationDescriptor(); assert superclass instanceof ClassDescriptor; superclassToSupertype.put((ClassDescriptor) superclass, supertype); } return superclassToSupertype; }
/** Returns document counts for each partition. */ Map<Object, Integer> getDocumentCountByPartition(List<Document> documents) { return ImmutableMap.copyOf( Maps.transformValues( getDocumentsByPartition(documents).asMap(), new Function<Collection<Document>, Integer>() { public Integer apply(Collection<Document> documents) { return documents.size(); } })); }
public List<Vertex> getProductAssociations(Vertex v) { Map<Vertex, Edge> adjacent = Maps.newHashMap(graph.row(v)); adjacent.putAll(graph.column(v)); return adjacent .entrySet() .stream() .sorted((e1, e2) -> e2.getValue().weight - e1.getValue().weight) .map(Map.Entry::getKey) .collect(Collectors.toList()); }
/** * Rest配置中心 * * @author Peter Zhang */ public class RestConfigurations { private static Map<String, RestServiceConfiguration> CONFIG_FOR_NAME = Maps.newHashMap(); private static Map<String, RestServiceConfiguration> CONFIG_FOR_SERVICE_NAME = Maps.newHashMap(); private static Map<String, RestServiceConfiguration> CONFIG_FOR_ENV = Maps.newHashMap(); private static RestServiceConfiguration DEFAULT_CONFIG; /** * 注册配置 * * @param configuration Rest服务基础配置 */ public static void register(RestServiceConfiguration configuration) { CONFIG_FOR_NAME.put(configuration.getConfigurationName(), configuration); CONFIG_FOR_SERVICE_NAME.put(configuration.getCgiServiceName(), configuration); CONFIG_FOR_ENV.put(configuration.getEnvironment(), configuration); DEFAULT_CONFIG = configuration; } /** 获取默认配置对象 */ public static RestServiceConfiguration getDefaultConfiguration() { return DEFAULT_CONFIG; } /** * 根据环境获取配置 * * @param environment 环境 */ public static RestServiceConfiguration getConfigByEnvironment(String environment) { return CONFIG_FOR_ENV.get(environment); } }
/** Stringifies a Map in a stable fashion. */ public static <K extends Comparable<K>, V> void toStringSorted( Map<K, V> map, StringBuilder builder) { builder.append('{'); List<Entry<K, V>> sortedProperties = Maps.sortedEntries(map); int index = 0; for (Entry<K, V> entry : sortedProperties) { if (index > 0) { builder.append(", "); } builder.append(entry.getKey()).append('=').append(entry.getValue()); index++; } builder.append('}'); }
/** * 按照对应关系对 aitID:threadInfo-> 1:n 对N从大到小排序 处理MulitMap中的数据,key:value->1:n 取出<key, n> * 对n降序排序后,取得序列后的List<Map.Entry<key, n>> * * @return */ public List<Map.Entry<String, Integer>> getOrderList(Multimap<String, ThreadInfo> w_IdMap) { Set<String> keys = w_IdMap.keySet(); Map<String, Integer> w_IdMappingThread = Maps.newHashMap(); for (String key : keys) { Collection<ThreadInfo> values = w_IdMap.get(key); w_IdMappingThread.put(key, values.size()); } List<Map.Entry<String, Integer>> orderList = new ArrayList<Map.Entry<String, Integer>>(w_IdMappingThread.entrySet()); Collections.sort( orderList, new Comparator<Map.Entry<String, Integer>>() { @Override public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) { return o2.getValue().compareTo(o1.getValue()); } }); return orderList; }
/** * Prepares a temporary attributes map for serialization purposes. Includes only the requested * elements in the map. */ private Map<String, Object> prepareAttributesForSerialization( boolean saveDocuments, boolean saveClusters, boolean saveOtherAttributes) { final Map<String, Object> tempAttributes = Maps.newHashMap(); if (saveOtherAttributes) { tempAttributes.putAll(attributes); tempAttributes.remove(AttributeNames.DOCUMENTS); tempAttributes.remove(AttributeNames.CLUSTERS); } else { tempAttributes.put(AttributeNames.QUERY, attributes.get(AttributeNames.QUERY)); } if (saveDocuments) { tempAttributes.put(AttributeNames.DOCUMENTS, getDocuments()); } if (saveClusters) { tempAttributes.put(AttributeNames.CLUSTERS, getClusters()); } return tempAttributes; }
@Override public Map<String, Object> getValuesDeep() { final Tag tag = this.findLastTag(this.path, false); if (!(tag instanceof CompoundTag)) { return Collections.emptyMap(); } final Queue<Node> node = new ArrayDeque<Node>( (Collection<? extends Node>) ImmutableList.of((Object) new Node(tag))); final Map<String, Object> values = (Map<String, Object>) Maps.newHashMap(); while (!node.isEmpty()) { final Node root = node.poll(); for (final Map.Entry<String, Tag> entry : root.values.entrySet()) { final String key = this.createRelativeKey(root.parent, entry.getKey()); if (entry.getValue() instanceof CompoundTag) { node.add(new Node(key, entry.getValue())); } else { values.put(key, entry.getValue().getValue()); } } } return values; }
/** Transfers document and cluster lists to the attributes map after deserialization. */ @Commit private void afterDeserialization() throws Exception { if (otherAttributesForSerialization != null) { attributes = SimpleXmlWrappers.unwrap(otherAttributesForSerialization); } attributesView = Collections.unmodifiableMap(attributes); attributes.put(AttributeNames.QUERY, query != null ? query.trim() : null); attributes.put(AttributeNames.DOCUMENTS, documents); attributes.put(AttributeNames.CLUSTERS, clusters); // Convert document ids to the actual references if (clusters != null && documents != null) { final Map<String, Document> documentsById = Maps.newHashMap(); for (Document document : documents) { documentsById.put(document.getStringId(), document); } for (Cluster cluster : clusters) { documentIdToReference(cluster, documentsById); } } }
/** * Represents one network of nodes, where each nodes is somehow connected to another within the * network. * * <p>Network contains following node types: - networking nodes - nodes that are a back-bone of a * network. These allow to connect multiple nodes in the network. A networking node "conducts" the * "signal" of the network to nodes defined in the "connectingOnSides" nodes in its vicinity. - leaf * nodes - nodes that are only receiving or producing a signal, and do not themselves "conduct" it * to other nodes. * * <p>A couple of non-obvious facts: 1. The same node (defined as location) cannot be both a * networking node and a leaf node in the same network. 2. The same leaf node can be a member of * multiple disjunctive networks (different network on each side). 3. A valid network can have no * networking nodes at all, and exactly two leaf nodes (neighbouring leaf nodes). * * @author Marcin Sciesinski <*****@*****.**> */ public class SimpleNetwork implements Network { private static final boolean SANITY_CHECK = false; private SetMultimap<ImmutableBlockLocation, NetworkNode> networkingNodes = HashMultimap.create(); private SetMultimap<ImmutableBlockLocation, NetworkNode> leafNodes = HashMultimap.create(); // Distance cache private Map<TwoNetworkNodes, Integer> distanceCache = Maps.newHashMap(); public static SimpleNetwork createDegenerateNetwork( NetworkNode networkNode1, NetworkNode networkNode2) { if (!areNodesConnecting(networkNode1, networkNode2)) throw new IllegalArgumentException("These two nodes are not connected"); SimpleNetwork network = new SimpleNetwork(); network.leafNodes.put(networkNode1.location, networkNode1); network.leafNodes.put(networkNode2.location, networkNode2); return network; } /** * Adds a networking node to the network. * * @param networkNode Definition of the networking node position and connecting sides. */ public void addNetworkingNode(NetworkNode networkNode) { if (SANITY_CHECK && !canAddNetworkingNode(networkNode)) throw new IllegalStateException("Unable to add this node to network"); networkingNodes.put(networkNode.location, networkNode); distanceCache.clear(); } /** * Adds a leaf node to the network. * * @param networkNode Definition of the leaf node position and connecting sides. */ public void addLeafNode(NetworkNode networkNode) { if (SANITY_CHECK && (!canAddLeafNode(networkNode) || isEmptyNetwork())) throw new IllegalStateException("Unable to add this node to network"); leafNodes.put(networkNode.location, networkNode); distanceCache.clear(); } /** * Returns the network size - a number of nodes it spans. If the same node is added twice with * different connecting sides, it is counted twice. * * @return The sum of networking nodes and leaf nodes (count). */ @Override public int getNetworkSize() { return networkingNodes.size() + leafNodes.size(); } /** * Removes a leaf node from the network. If this removal made the network degenerate, it will * return <code>true</code>. * * @param networkingNode Definition of the leaf node position and connecting sides. * @return <code>true</code> if the network after the removal is degenerated or empty (no longer * valid). */ public boolean removeLeafNode(NetworkNode networkingNode) { // Removal of a leaf node cannot split the network, so it's just safe to remove it // We just need to check, if after removal of the node, network becomes degenerated, if so - we // need // to signal that the network is no longer valid and should be removed. final boolean changed = leafNodes.remove(networkingNode.location, networkingNode); if (!changed) throw new IllegalStateException("Tried to remove a node that is not in the network"); distanceCache.clear(); return isDegeneratedNetwork() || isEmptyNetwork(); } public void removeAllLeafNodes() { leafNodes.clear(); distanceCache.clear(); } public void removeAllNetworkingNodes() { networkingNodes.clear(); distanceCache.clear(); } public void removeNetworkingNode(NetworkNode networkNode) { if (!networkingNodes.remove(networkNode.location, networkNode)) throw new IllegalStateException("Tried to remove a node that is not in the network"); distanceCache.clear(); } public Collection<NetworkNode> getNetworkingNodes() { return Collections.unmodifiableCollection(networkingNodes.values()); } public Collection<NetworkNode> getLeafNodes() { return Collections.unmodifiableCollection(leafNodes.values()); } public static boolean areNodesConnecting(NetworkNode node1, NetworkNode node2) { for (Side side : SideBitFlag.getSides(node1.connectionSides)) { final ImmutableBlockLocation possibleConnectedLocation = node1.location.move(side); if (node2.location.equals(possibleConnectedLocation) && SideBitFlag.hasSide(node2.connectionSides, side.reverse())) return true; } return false; } /** * If this network can connect to node at the location specified with the specified connecting * sides. * * @param networkNode Definition of the networking node position and connecting sides. * @return If the networking node can be added to the network (connects to it). */ public boolean canAddNetworkingNode(NetworkNode networkNode) { if (isEmptyNetwork()) return true; if (networkingNodes.containsValue(networkNode) || leafNodes.containsValue(networkNode)) return false; return canConnectToNetworkingNode(networkNode); } public boolean canAddLeafNode(NetworkNode networkNode) { if (isEmptyNetwork()) return false; if (networkingNodes.containsValue(networkNode) || leafNodes.containsValue(networkNode)) return false; return canConnectToNetworkingNode(networkNode); } private boolean canConnectToNetworkingNode(NetworkNode networkNode) { for (Side connectingOnSide : SideBitFlag.getSides(networkNode.connectionSides)) { final ImmutableBlockLocation possibleConnectionLocation = networkNode.location.move(connectingOnSide); for (NetworkNode possibleConnectedNode : networkingNodes.get(possibleConnectionLocation)) { if (SideBitFlag.hasSide(possibleConnectedNode.connectionSides, connectingOnSide.reverse())) return true; } } return false; } @Override public boolean hasNetworkingNode(NetworkNode networkNode) { return networkingNodes.containsValue(networkNode); } @Override public boolean hasLeafNode(NetworkNode networkNode) { return leafNodes.containsValue(networkNode); } @Override public int getDistance(NetworkNode from, NetworkNode to) { TwoNetworkNodes nodePair = new TwoNetworkNodes(from, to); final Integer cachedDistance = distanceCache.get(nodePair); if (cachedDistance != null) return cachedDistance; if ((!hasNetworkingNode(from) && !hasLeafNode(from)) || (!hasNetworkingNode(to) && !hasLeafNode(to))) throw new IllegalArgumentException("Cannot test nodes not in network"); if (from.equals(to)) return 0; if (SimpleNetwork.areNodesConnecting(from, to)) return 1; // Breadth-first search of the network Set<NetworkNode> visitedNodes = Sets.newHashSet(); visitedNodes.add(from); Set<NetworkNode> networkingNodesToTest = Sets.newHashSet(); listConnectedNotVisitedNetworkingNodes(visitedNodes, from, networkingNodesToTest); int distanceSearched = 1; while (networkingNodesToTest.size() > 0) { distanceSearched++; for (NetworkNode nodeToTest : networkingNodesToTest) { if (SimpleNetwork.areNodesConnecting(nodeToTest, to)) { distanceCache.put(new TwoNetworkNodes(from, to), distanceSearched); return distanceSearched; } visitedNodes.add(nodeToTest); } Set<NetworkNode> nextNetworkingNodesToTest = Sets.newHashSet(); for (NetworkNode nodeToTest : networkingNodesToTest) listConnectedNotVisitedNetworkingNodes(visitedNodes, nodeToTest, nextNetworkingNodesToTest); networkingNodesToTest = nextNetworkingNodesToTest; } return -1; } @Override public boolean isInDistance(int distance, NetworkNode from, NetworkNode to) { if (distance < 0) throw new IllegalArgumentException("distance must be >= 0"); TwoNetworkNodes nodePair = new TwoNetworkNodes(from, to); final Integer cachedDistance = distanceCache.get(nodePair); if (cachedDistance != null) return cachedDistance <= distance; if ((!hasNetworkingNode(from) && !hasLeafNode(from)) || (!hasNetworkingNode(to) && !hasLeafNode(to))) throw new IllegalArgumentException("Cannot test nodes not in network"); return isInDistanceInternal(distance, from, to, nodePair); } private boolean isInDistanceInternal( int distance, NetworkNode from, NetworkNode to, TwoNetworkNodes cachePairKey) { if (from.equals(to)) return true; if (distance == 0) return false; if (SimpleNetwork.areNodesConnecting(from, to)) return true; // Breadth-first search of the network Set<NetworkNode> visitedNodes = Sets.newHashSet(); visitedNodes.add(from); Set<NetworkNode> networkingNodesToTest = Sets.newHashSet(); listConnectedNotVisitedNetworkingNodes(visitedNodes, from, networkingNodesToTest); int distanceSearched = 1; while (distanceSearched < distance) { distanceSearched++; for (NetworkNode nodeToTest : networkingNodesToTest) { if (SimpleNetwork.areNodesConnecting(nodeToTest, to)) { distanceCache.put(cachePairKey, distanceSearched); return true; } visitedNodes.add(nodeToTest); } Set<NetworkNode> nextNetworkingNodesToTest = Sets.newHashSet(); for (NetworkNode nodeToTest : networkingNodesToTest) listConnectedNotVisitedNetworkingNodes(visitedNodes, nodeToTest, nextNetworkingNodesToTest); networkingNodesToTest = nextNetworkingNodesToTest; } return false; } @Override public boolean isInDistanceWithSide(int distance, NetworkNode from, NetworkNode to, Side toSide) { to = new NetworkNode(to.location.toVector3i(), toSide); TwoNetworkNodes nodePair = new TwoNetworkNodes(from, to); return isInDistanceInternal(distance, from, to, nodePair); } @Override public byte getLeafSidesInNetwork(NetworkNode networkNode) { if (!hasLeafNode(networkNode)) throw new IllegalArgumentException("Cannot test nodes not in network"); if (networkingNodes.size() == 0) { // Degenerated network for (Side connectingOnSide : SideBitFlag.getSides(networkNode.connectionSides)) { Vector3i possibleLocation = networkNode.location.toVector3i(); possibleLocation.add(connectingOnSide.getVector3i()); for (NetworkNode node : leafNodes.get(new ImmutableBlockLocation(possibleLocation))) { if (SideBitFlag.hasSide(node.connectionSides, connectingOnSide.reverse())) { return SideBitFlag.getSide(connectingOnSide); } } } return 0; } else { byte result = 0; for (Side connectingOnSide : SideBitFlag.getSides(networkNode.connectionSides)) { Vector3i possibleLocation = networkNode.location.toVector3i(); possibleLocation.add(connectingOnSide.getVector3i()); for (NetworkNode node : networkingNodes.get(new ImmutableBlockLocation(possibleLocation))) { if (SideBitFlag.hasSide(node.connectionSides, connectingOnSide.reverse())) { result += SideBitFlag.getSide(connectingOnSide); } } } return result; } } // public Map<Vector3i, Byte> getConnectedNodes(Vector3i location, byte connectionSides) { // Map<Vector3i, Byte> result = Maps.newHashMap(); // for (Direction connectingOnSide : DirectionsUtil.getDirections(connectionSides)) { // final Vector3i possibleNodeLocation = new Vector3i(location); // possibleNodeLocation.add(connectingOnSide.getVector3i()); // // final Byte directionsForNodeOnThatSide = networkingNodes.get(possibleNodeLocation); // if (directionsForNodeOnThatSide != null && // DirectionsUtil.hasDirection(directionsForNodeOnThatSide, connectingOnSide.reverse())) // result.put(possibleNodeLocation, directionsForNodeOnThatSide); // // for (byte directionsForLeafNodeOnThatSide : leafNodes.get(possibleNodeLocation)) { // if (DirectionsUtil.hasDirection(directionsForLeafNodeOnThatSide, // connectingOnSide.reverse())) // result.put(possibleNodeLocation, directionsForLeafNodeOnThatSide); // } // } // return result; // } private void listConnectedNotVisitedNetworkingNodes( Set<NetworkNode> visitedNodes, NetworkNode location, Collection<NetworkNode> result) { for (Side connectingOnSide : SideBitFlag.getSides(location.connectionSides)) { final ImmutableBlockLocation possibleConnectionLocation = location.location.move(connectingOnSide); for (NetworkNode possibleConnection : networkingNodes.get(possibleConnectionLocation)) { if (!visitedNodes.contains(possibleConnection) && SideBitFlag.hasSide(possibleConnection.connectionSides, connectingOnSide.reverse())) result.add(possibleConnection); } } } private boolean isDegeneratedNetwork() { return networkingNodes.isEmpty() && leafNodes.size() == 1; } private boolean isEmptyNetwork() { return networkingNodes.isEmpty() && leafNodes.isEmpty(); } }
public class PseudocodeImpl implements Pseudocode { public class PseudocodeLabel implements Label { private final String name; private final String comment; private Integer targetInstructionIndex; private PseudocodeLabel(@NotNull String name, @Nullable String comment) { this.name = name; this.comment = comment; } @NotNull @Override public String getName() { return name; } @Override public String toString() { return comment == null ? name : (name + " [" + comment + "]"); } public Integer getTargetInstructionIndex() { return targetInstructionIndex; } public void setTargetInstructionIndex(int targetInstructionIndex) { this.targetInstructionIndex = targetInstructionIndex; } @Nullable private List<Instruction> resolve() { assert targetInstructionIndex != null; return mutableInstructionList.subList( getTargetInstructionIndex(), mutableInstructionList.size()); } public Instruction resolveToInstruction() { assert targetInstructionIndex != null; return mutableInstructionList.get(targetInstructionIndex); } public PseudocodeLabel copy(int newLabelIndex) { return new PseudocodeLabel("L" + newLabelIndex, "copy of " + name + ", " + comment); } public PseudocodeImpl getPseudocode() { return PseudocodeImpl.this; } } private final List<Instruction> mutableInstructionList = new ArrayList<Instruction>(); private final List<Instruction> instructions = new ArrayList<Instruction>(); private final BidirectionalMap<KtElement, PseudoValue> elementsToValues = new BidirectionalMap<KtElement, PseudoValue>(); private final Map<PseudoValue, List<Instruction>> valueUsages = Maps.newHashMap(); private final Map<PseudoValue, Set<PseudoValue>> mergedValues = Maps.newHashMap(); private final Set<Instruction> sideEffectFree = Sets.newHashSet(); private Pseudocode parent = null; private Set<LocalFunctionDeclarationInstruction> localDeclarations = null; // todo getters private final Map<KtElement, Instruction> representativeInstructions = new HashMap<KtElement, Instruction>(); private final List<PseudocodeLabel> labels = new ArrayList<PseudocodeLabel>(); private final KtElement correspondingElement; private SubroutineExitInstruction exitInstruction; private SubroutineSinkInstruction sinkInstruction; private SubroutineExitInstruction errorInstruction; private boolean postPrecessed = false; public PseudocodeImpl(KtElement correspondingElement) { this.correspondingElement = correspondingElement; } @NotNull @Override public KtElement getCorrespondingElement() { return correspondingElement; } @NotNull @Override public Set<LocalFunctionDeclarationInstruction> getLocalDeclarations() { if (localDeclarations == null) { localDeclarations = getLocalDeclarations(this); } return localDeclarations; } @NotNull private static Set<LocalFunctionDeclarationInstruction> getLocalDeclarations( @NotNull Pseudocode pseudocode) { Set<LocalFunctionDeclarationInstruction> localDeclarations = Sets.newLinkedHashSet(); for (Instruction instruction : ((PseudocodeImpl) pseudocode).mutableInstructionList) { if (instruction instanceof LocalFunctionDeclarationInstruction) { localDeclarations.add((LocalFunctionDeclarationInstruction) instruction); localDeclarations.addAll( getLocalDeclarations(((LocalFunctionDeclarationInstruction) instruction).getBody())); } } return localDeclarations; } @Override @Nullable public Pseudocode getParent() { return parent; } private void setParent(Pseudocode parent) { this.parent = parent; } @NotNull public Pseudocode getRootPseudocode() { Pseudocode parent = getParent(); while (parent != null) { if (parent.getParent() == null) return parent; parent = parent.getParent(); } return this; } /*package*/ PseudocodeLabel createLabel(@NotNull String name, @Nullable String comment) { PseudocodeLabel label = new PseudocodeLabel(name, comment); labels.add(label); return label; } @Override @NotNull public List<Instruction> getInstructions() { return instructions; } @NotNull @Override public List<Instruction> getReversedInstructions() { LinkedHashSet<Instruction> traversedInstructions = Sets.newLinkedHashSet(); PseudocodeTraverserKt.traverseFollowingInstructions( sinkInstruction, traversedInstructions, BACKWARD, null); if (traversedInstructions.size() < instructions.size()) { List<Instruction> simplyReversedInstructions = Lists.newArrayList(instructions); Collections.reverse(simplyReversedInstructions); for (Instruction instruction : simplyReversedInstructions) { if (!traversedInstructions.contains(instruction)) { PseudocodeTraverserKt.traverseFollowingInstructions( instruction, traversedInstructions, BACKWARD, null); } } } return Lists.newArrayList(traversedInstructions); } @Override @NotNull public List<Instruction> getInstructionsIncludingDeadCode() { return mutableInstructionList; } // for tests only @NotNull public List<PseudocodeLabel> getLabels() { return labels; } /*package*/ void addExitInstruction(SubroutineExitInstruction exitInstruction) { addInstruction(exitInstruction); assert this.exitInstruction == null; this.exitInstruction = exitInstruction; } /*package*/ void addSinkInstruction(SubroutineSinkInstruction sinkInstruction) { addInstruction(sinkInstruction); assert this.sinkInstruction == null; this.sinkInstruction = sinkInstruction; } /*package*/ void addErrorInstruction(SubroutineExitInstruction errorInstruction) { addInstruction(errorInstruction); assert this.errorInstruction == null; this.errorInstruction = errorInstruction; } /*package*/ void addInstruction(Instruction instruction) { mutableInstructionList.add(instruction); instruction.setOwner(this); if (instruction instanceof KtElementInstruction) { KtElementInstruction elementInstruction = (KtElementInstruction) instruction; representativeInstructions.put(elementInstruction.getElement(), instruction); } if (instruction instanceof MergeInstruction) { addMergedValues((MergeInstruction) instruction); } for (PseudoValue inputValue : instruction.getInputValues()) { addValueUsage(inputValue, instruction); for (PseudoValue mergedValue : getMergedValues(inputValue)) { addValueUsage(mergedValue, instruction); } } if (PseudocodeUtilsKt.calcSideEffectFree(instruction)) { sideEffectFree.add(instruction); } } @Override @NotNull public SubroutineExitInstruction getExitInstruction() { return exitInstruction; } @Override @NotNull public SubroutineSinkInstruction getSinkInstruction() { return sinkInstruction; } @Override @NotNull public SubroutineEnterInstruction getEnterInstruction() { return (SubroutineEnterInstruction) mutableInstructionList.get(0); } @Nullable @Override public PseudoValue getElementValue(@Nullable KtElement element) { return elementsToValues.get(element); } @NotNull @Override public List<? extends KtElement> getValueElements(@Nullable PseudoValue value) { List<? extends KtElement> result = elementsToValues.getKeysByValue(value); return result != null ? result : Collections.<KtElement>emptyList(); } @NotNull @Override public List<? extends Instruction> getUsages(@Nullable PseudoValue value) { List<? extends Instruction> result = valueUsages.get(value); return result != null ? result : Collections.<Instruction>emptyList(); } @Override public boolean isSideEffectFree(@NotNull Instruction instruction) { return sideEffectFree.contains(instruction); } /*package*/ void bindElementToValue(@NotNull KtElement element, @NotNull PseudoValue value) { elementsToValues.put(element, value); } /*package*/ void bindLabel(Label label) { ((PseudocodeLabel) label).setTargetInstructionIndex(mutableInstructionList.size()); } private Set<PseudoValue> getMergedValues(@NotNull PseudoValue value) { Set<PseudoValue> result = mergedValues.get(value); return result != null ? result : Collections.<PseudoValue>emptySet(); } private void addMergedValues(@NotNull MergeInstruction instruction) { Set<PseudoValue> result = new LinkedHashSet<PseudoValue>(); for (PseudoValue value : instruction.getInputValues()) { result.addAll(getMergedValues(value)); result.add(value); } mergedValues.put(instruction.getOutputValue(), result); } private void addValueUsage(PseudoValue value, Instruction usage) { if (usage instanceof MergeInstruction) return; MapsKt.getOrPut( valueUsages, value, new Function0<List<Instruction>>() { @Override public List<Instruction> invoke() { return Lists.newArrayList(); } }) .add(usage); } public void postProcess() { if (postPrecessed) return; postPrecessed = true; errorInstruction.setSink(getSinkInstruction()); exitInstruction.setSink(getSinkInstruction()); int index = 0; for (Instruction instruction : mutableInstructionList) { // recursively invokes 'postProcess' for local declarations processInstruction(instruction, index); index++; } if (getParent() != null) return; // Collecting reachable instructions should be done after processing all instructions // (including instructions in local declarations) to avoid being in incomplete state. collectAndCacheReachableInstructions(); for (LocalFunctionDeclarationInstruction localFunctionDeclarationInstruction : getLocalDeclarations()) { ((PseudocodeImpl) localFunctionDeclarationInstruction.getBody()) .collectAndCacheReachableInstructions(); } } private void collectAndCacheReachableInstructions() { Set<Instruction> reachableInstructions = collectReachableInstructions(); for (Instruction instruction : mutableInstructionList) { if (reachableInstructions.contains(instruction)) { instructions.add(instruction); } } markDeadInstructions(); } private void processInstruction(Instruction instruction, final int currentPosition) { instruction.accept( new InstructionVisitor() { @Override public void visitInstructionWithNext(@NotNull InstructionWithNext instruction) { instruction.setNext(getNextPosition(currentPosition)); } @Override public void visitJump(@NotNull AbstractJumpInstruction instruction) { instruction.setResolvedTarget(getJumpTarget(instruction.getTargetLabel())); } @Override public void visitNondeterministicJump( @NotNull NondeterministicJumpInstruction instruction) { instruction.setNext(getNextPosition(currentPosition)); List<Label> targetLabels = instruction.getTargetLabels(); for (Label targetLabel : targetLabels) { instruction.setResolvedTarget(targetLabel, getJumpTarget(targetLabel)); } } @Override public void visitConditionalJump(@NotNull ConditionalJumpInstruction instruction) { Instruction nextInstruction = getNextPosition(currentPosition); Instruction jumpTarget = getJumpTarget(instruction.getTargetLabel()); if (instruction.getOnTrue()) { instruction.setNextOnFalse(nextInstruction); instruction.setNextOnTrue(jumpTarget); } else { instruction.setNextOnFalse(jumpTarget); instruction.setNextOnTrue(nextInstruction); } visitJump(instruction); } @Override public void visitLocalFunctionDeclarationInstruction( @NotNull LocalFunctionDeclarationInstruction instruction) { PseudocodeImpl body = (PseudocodeImpl) instruction.getBody(); body.setParent(PseudocodeImpl.this); body.postProcess(); instruction.setNext(getSinkInstruction()); } @Override public void visitSubroutineExit(@NotNull SubroutineExitInstruction instruction) { // Nothing } @Override public void visitSubroutineSink(@NotNull SubroutineSinkInstruction instruction) { // Nothing } @Override public void visitInstruction(@NotNull Instruction instruction) { throw new UnsupportedOperationException(instruction.toString()); } }); } private Set<Instruction> collectReachableInstructions() { Set<Instruction> visited = Sets.newHashSet(); PseudocodeTraverserKt.traverseFollowingInstructions( getEnterInstruction(), visited, FORWARD, null); if (!visited.contains(getExitInstruction())) { visited.add(getExitInstruction()); } if (!visited.contains(errorInstruction)) { visited.add(errorInstruction); } if (!visited.contains(getSinkInstruction())) { visited.add(getSinkInstruction()); } return visited; } private void markDeadInstructions() { Set<Instruction> instructionSet = Sets.newHashSet(instructions); for (Instruction instruction : mutableInstructionList) { if (!instructionSet.contains(instruction)) { ((InstructionImpl) instruction).setMarkedAsDead(true); for (Instruction nextInstruction : instruction.getNextInstructions()) { nextInstruction.getPreviousInstructions().remove(instruction); } } } } @NotNull private Instruction getJumpTarget(@NotNull Label targetLabel) { return ((PseudocodeLabel) targetLabel).resolveToInstruction(); } @NotNull private Instruction getNextPosition(int currentPosition) { int targetPosition = currentPosition + 1; assert targetPosition < mutableInstructionList.size() : currentPosition; return mutableInstructionList.get(targetPosition); } @Override public PseudocodeImpl copy() { PseudocodeImpl result = new PseudocodeImpl(correspondingElement); result.repeatWhole(this); return result; } private void repeatWhole(@NotNull PseudocodeImpl originalPseudocode) { repeatInternal(originalPseudocode, null, null, 0); parent = originalPseudocode.parent; } public int repeatPart(@NotNull Label startLabel, @NotNull Label finishLabel, int labelCount) { return repeatInternal( ((PseudocodeLabel) startLabel).getPseudocode(), startLabel, finishLabel, labelCount); } private int repeatInternal( @NotNull PseudocodeImpl originalPseudocode, @Nullable Label startLabel, @Nullable Label finishLabel, int labelCount) { Integer startIndex = startLabel != null ? ((PseudocodeLabel) startLabel).getTargetInstructionIndex() : Integer.valueOf(0); assert startIndex != null; Integer finishIndex = finishLabel != null ? ((PseudocodeLabel) finishLabel).getTargetInstructionIndex() : Integer.valueOf(originalPseudocode.mutableInstructionList.size()); assert finishIndex != null; Map<Label, Label> originalToCopy = Maps.newLinkedHashMap(); Multimap<Instruction, Label> originalLabelsForInstruction = HashMultimap.create(); for (PseudocodeLabel label : originalPseudocode.labels) { Integer index = label.getTargetInstructionIndex(); if (index == null) continue; // label is not bounded yet if (label == startLabel || label == finishLabel) continue; if (startIndex <= index && index <= finishIndex) { originalToCopy.put(label, label.copy(labelCount++)); originalLabelsForInstruction.put(getJumpTarget(label), label); } } for (Label label : originalToCopy.values()) { labels.add((PseudocodeLabel) label); } for (int index = startIndex; index < finishIndex; index++) { Instruction originalInstruction = originalPseudocode.mutableInstructionList.get(index); repeatLabelsBindingForInstruction( originalInstruction, originalToCopy, originalLabelsForInstruction); Instruction copy = copyInstruction(originalInstruction, originalToCopy); addInstruction(copy); if (originalInstruction == originalPseudocode.errorInstruction && copy instanceof SubroutineExitInstruction) { errorInstruction = (SubroutineExitInstruction) copy; } if (originalInstruction == originalPseudocode.exitInstruction && copy instanceof SubroutineExitInstruction) { exitInstruction = (SubroutineExitInstruction) copy; } if (originalInstruction == originalPseudocode.sinkInstruction && copy instanceof SubroutineSinkInstruction) { sinkInstruction = (SubroutineSinkInstruction) copy; } } if (finishIndex < mutableInstructionList.size()) { repeatLabelsBindingForInstruction( originalPseudocode.mutableInstructionList.get(finishIndex), originalToCopy, originalLabelsForInstruction); } return labelCount; } private void repeatLabelsBindingForInstruction( @NotNull Instruction originalInstruction, @NotNull Map<Label, Label> originalToCopy, @NotNull Multimap<Instruction, Label> originalLabelsForInstruction) { for (Label originalLabel : originalLabelsForInstruction.get(originalInstruction)) { bindLabel(originalToCopy.get(originalLabel)); } } private static Instruction copyInstruction( @NotNull Instruction instruction, @NotNull Map<Label, Label> originalToCopy) { if (instruction instanceof AbstractJumpInstruction) { Label originalTarget = ((AbstractJumpInstruction) instruction).getTargetLabel(); if (originalToCopy.containsKey(originalTarget)) { return ((AbstractJumpInstruction) instruction).copy(originalToCopy.get(originalTarget)); } } if (instruction instanceof NondeterministicJumpInstruction) { List<Label> originalTargets = ((NondeterministicJumpInstruction) instruction).getTargetLabels(); List<Label> copyTargets = copyLabels(originalTargets, originalToCopy); return ((NondeterministicJumpInstruction) instruction).copy(copyTargets); } return ((InstructionImpl) instruction).copy(); } @NotNull private static List<Label> copyLabels( Collection<Label> labels, Map<Label, Label> originalToCopy) { List<Label> newLabels = Lists.newArrayList(); for (Label label : labels) { Label newLabel = originalToCopy.get(label); newLabels.add(newLabel != null ? newLabel : label); } return newLabels; } }
/** * Encapsulates the results of processing. Provides access to the values of attributes collected * after processing and utility methods for obtaining processed documents ( {@link * #getDocuments()})) and the created clusters ({@link #getClusters()}). */ @Root(name = "searchresult", strict = false) public final class ProcessingResult { /** Attributes collected after processing */ private Map<String, Object> attributes = Maps.newHashMap(); /** Read-only view of attributes exposed in {@link #getAttributes()} */ private Map<String, Object> attributesView; /** * Query field used during serialization/ deserialization, see {@link #afterDeserialization()} and * {@link #beforeSerialization()} */ @Element(required = false) private String query; /** * Documents field used during serialization/ deserialization, see {@link #afterDeserialization()} * and {@link #beforeSerialization()} */ @ElementList(inline = true, required = false) private List<Document> documents; /** * Clusters field used during serialization/ deserialization, see {@link #afterDeserialization()} * and {@link #beforeSerialization()} */ @ElementList(inline = true, required = false) private List<Cluster> clusters; /** Attributes of this result for serialization/ deserialization purposes. */ @ElementMap(entry = "attribute", key = "key", attribute = true, inline = true, required = false) private HashMap<String, SimpleXmlWrapperValue> otherAttributesForSerialization; /** Parameterless constructor required for XML serialization/ deserialization. */ ProcessingResult() { this(new HashMap<String, Object>()); } /** * Creates a {@link ProcessingResult} with the provided <code>attributes</code>. Assigns unique * document identifiers if documents are present in the <code>attributes</code> map (under the key * {@link AttributeNames#DOCUMENTS}). */ @SuppressWarnings("unchecked") ProcessingResult(Map<String, Object> attributes) { this.attributes = attributes; // Replace a modifiable collection of documents with an unmodifiable one final List<Document> documents = (List<Document>) attributes.get(AttributeNames.DOCUMENTS); if (documents != null) { Document.assignDocumentIds(documents); attributes.put(AttributeNames.DOCUMENTS, Collections.unmodifiableList(documents)); } // Replace a modifiable collection of clusters with an unmodifiable one final List<Cluster> clusters = (List<Cluster>) attributes.get(AttributeNames.CLUSTERS); if (clusters != null) { Cluster.assignClusterIds(clusters); attributes.put(AttributeNames.CLUSTERS, Collections.unmodifiableList(clusters)); } // Store a reference to attributes as an unmodifiable map this.attributesView = Collections.unmodifiableMap(attributes); } /** * Returns attributes fed-in and collected during processing. The returned map is unmodifiable. * * @return attributes fed-in and collected during processing */ public Map<String, Object> getAttributes() { return attributesView; } /** * Returns a specific attribute of this result set. This method is equivalent to calling {@link * #getAttributes()} and then getting the required attribute from the map. * * @param key key of the attribute to return * @return value of the attribute */ @SuppressWarnings("unchecked") public <T> T getAttribute(String key) { return (T) attributesView.get(key); } /** * Returns the documents that have been processed. The returned collection is unmodifiable. * * @return documents that have been processed or <code>null</code> if no documents are present in * the result. */ @SuppressWarnings("unchecked") public List<Document> getDocuments() { return (List<Document>) attributes.get(AttributeNames.DOCUMENTS); } /* * TODO: Returning a list of clusters instead of a (possibly artificial) cluster with * subclusters adds a little complexity to recursive methods operating on clusters (a * natural entry point is a method taking one cluster and acting on subclusters * recursively). If we have to start with a list of clusters, we have to handle this * special case separately... */ /** * Returns the clusters that have been created during processing. The returned list is * unmodifiable. * * @return clusters created during processing or <code>null</code> if no clusters were present in * the result. */ @SuppressWarnings("unchecked") public List<Cluster> getClusters() { return (List<Cluster>) attributes.get(AttributeNames.CLUSTERS); } /** Extracts document and cluster lists before serialization. */ @Persist private void beforeSerialization() { /* * See http://issues.carrot2.org/browse/CARROT-693; this monitor does not save us * in multi-threaded environment anyway. A better solution would be to prepare * this eagerly in the constructor, but we try to balance overhead and full * correctness here. */ synchronized (this) { query = (String) attributes.get(AttributeNames.QUERY); if (getDocuments() != null) { documents = Lists.newArrayList(getDocuments()); } else { documents = null; } if (getClusters() != null) { clusters = Lists.newArrayList(getClusters()); } else { clusters = null; } otherAttributesForSerialization = MapUtils.asHashMap(SimpleXmlWrappers.wrap(attributes)); otherAttributesForSerialization.remove(AttributeNames.QUERY); otherAttributesForSerialization.remove(AttributeNames.CLUSTERS); otherAttributesForSerialization.remove(AttributeNames.DOCUMENTS); if (otherAttributesForSerialization.isEmpty()) { otherAttributesForSerialization = null; } } } /** Transfers document and cluster lists to the attributes map after deserialization. */ @Commit private void afterDeserialization() throws Exception { if (otherAttributesForSerialization != null) { attributes = SimpleXmlWrappers.unwrap(otherAttributesForSerialization); } attributesView = Collections.unmodifiableMap(attributes); attributes.put(AttributeNames.QUERY, query != null ? query.trim() : null); attributes.put(AttributeNames.DOCUMENTS, documents); attributes.put(AttributeNames.CLUSTERS, clusters); // Convert document ids to the actual references if (clusters != null && documents != null) { final Map<String, Document> documentsById = Maps.newHashMap(); for (Document document : documents) { documentsById.put(document.getStringId(), document); } for (Cluster cluster : clusters) { documentIdToReference(cluster, documentsById); } } } /** Replace document refids with the actual references upon deserialization. */ private void documentIdToReference(Cluster cluster, Map<String, Document> documents) { if (cluster.documentIds != null) { for (Cluster.DocumentRefid documentRefid : cluster.documentIds) { cluster.addDocuments(documents.get(documentRefid.refid)); } } for (Cluster subcluster : cluster.getSubclusters()) { documentIdToReference(subcluster, documents); } } /** Serializes this {@link ProcessingResult} to an XML string. */ public String serialize() { try { StringWriter sw = new StringWriter(); new Persister().write(this, sw); return sw.toString(); } catch (Exception e) { throw new RuntimeException(e); } } /** * Serializes this {@link ProcessingResult} to an XML stream. The output includes all documents, * clusters and other attributes. * * <p>This method is not thread-safe, external synchronization must be applied if needed. * * @param stream the stream to serialize this {@link ProcessingResult} to. The stream will * <strong>not</strong> be closed. * @throws Exception in case of any problems with serialization */ public void serialize(OutputStream stream) throws Exception { serialize(stream, true, true); } /** * Serializes this {@link ProcessingResult} to a byte stream. Documents and clusters can be * included or skipped in the output as requested. Other attributes are always included. * * <p>This method is not thread-safe, external synchronization must be applied if needed. * * @param stream the stream to serialize this {@link ProcessingResult} to. The stream will * <strong>not</strong> be closed. * @param saveDocuments if <code>false</code>, documents will not be serialized. Notice that when * deserializing XML containing clusters but not documents, document references in {@link * Cluster#getDocuments()} will not be restored. * @param saveClusters if <code>false</code>, clusters will not be serialized * @throws Exception in case of any problems with serialization */ public void serialize(OutputStream stream, boolean saveDocuments, boolean saveClusters) throws Exception { serialize(stream, saveDocuments, saveClusters, true); } /** * Serializes this {@link ProcessingResult} to a byte stream. Documents, clusters and other * attributes can be included or skipped in the output as requested. * * <p>This method is not thread-safe, external synchronization must be applied if needed. * * @param stream the stream to serialize this {@link ProcessingResult} to. The stream will * <strong>not</strong> be closed. * @param saveDocuments if <code>false</code>, documents will not be serialized. Notice that when * deserializing XML containing clusters but not documents, document references in {@link * Cluster#getDocuments()} will not be restored. * @param saveClusters if <code>false</code>, clusters will not be serialized * @param saveOtherAttributes if <code>false</code>, other attributes will not be serialized * @throws Exception in case of any problems with serialization */ public void serialize( OutputStream stream, boolean saveDocuments, boolean saveClusters, boolean saveOtherAttributes) throws Exception { final Map<String, Object> backupAttributes = attributes; attributes = prepareAttributesForSerialization(saveDocuments, saveClusters, saveOtherAttributes); new Persister().write(this, stream); attributes = backupAttributes; } /** Deserialize from an input stream of characters. */ public static ProcessingResult deserialize(CharSequence input) throws Exception { return new Persister().read(ProcessingResult.class, input.toString()); } /** * Deserializes a {@link ProcessingResult} from an XML stream. * * @param input the input XML stream to deserialize a {@link ProcessingResult} from. The stream * will <strong>not</strong> be closed. * @return deserialized {@link ProcessingResult} * @throws Exception is case of any problems with deserialization */ public static ProcessingResult deserialize(InputStream input) throws Exception { return new Persister().read(ProcessingResult.class, input); } /** * Serializes this processing result as JSON to the provided <code>writer</code>. The output * includes all documents, clusters and other attributes. * * <p>This method is not thread-safe, external synchronization must be applied if needed. * * @param writer the writer to serialize this processing result to. The writer will * <strong>not</strong> be closed. * @throws IOException in case of any problems with serialization */ public void serializeJson(Writer writer) throws IOException { serializeJson(writer, null); } /** * Serializes this processing result as JSON to the provided <code>writer</code>. The output * includes all documents, clusters and other attributes. * * <p>This method is not thread-safe, external synchronization must be applied if needed. * * @param writer the writer to serialize this processing result to. The writer will * <strong>not</strong> be closed. * @param callback JavaScript function name in which to wrap the JSON response or <code>null * </code>. * @throws IOException in case of any problems with serialization */ public void serializeJson(Writer writer, String callback) throws IOException { serializeJson(writer, callback, true, true); } /** * Serializes this processing result as JSON to the provided <code>writer</code>. Documents and * clusters can be included or skipped in the output as requested. Other attributes are always * included. * * <p>This method is not thread-safe, external synchronization must be applied if needed. * * @param writer the writer to serialize this processing result to. The writer will * <strong>not</strong> be closed. * @param callback JavaScript function name in which to wrap the JSON response or <code>null * </code>. * @param saveDocuments if <code>false</code>, documents will not be serialized. * @param saveClusters if <code>false</code>, clusters will not be serialized * @throws IOException in case of any problems with serialization */ public void serializeJson( Writer writer, String callback, boolean saveDocuments, boolean saveClusters) throws IOException { serializeJson(writer, callback, false, saveDocuments, saveClusters); } /** * Serializes this processing result as JSON to the provided <code>writer</code>. * * <p>This method is not thread-safe, external synchronization must be applied if needed. * * @param writer the writer to serialize this processing result to. The writer will * <strong>not</strong> be closed. * @param callback JavaScript function name in which to wrap the JSON response or <code>null * </code>. * @param indent if <code>true</code>, the output JSON will be pretty-printed * @param saveDocuments if <code>false</code>, documents will not be serialized. * @param saveClusters if <code>false</code>, clusters will not be serialized * @throws IOException in case of any problems with serialization */ public void serializeJson( Writer writer, String callback, boolean indent, boolean saveDocuments, boolean saveClusters) throws IOException { serializeJson(writer, callback, indent, saveDocuments, saveClusters, true); } /** * Serializes this processing result as JSON to the provided <code>writer</code>. Documents, * clusters and other attributes can be included or skipped in the output as requested. * * @param writer the writer to serialize this processing result to. The writer will * <strong>not</strong> be closed. * @param callback JavaScript function name in which to wrap the JSON response or <code>null * </code>. * @param indent if <code>true</code>, the output JSON will be pretty-printed * @param saveDocuments if <code>false</code>, documents will not be serialized. * @param saveClusters if <code>false</code>, clusters will not be serialized * @param saveOtherAttributes if <code>false</code>, other attributes will not be serialized * @throws IOException in case of any problems with serialization */ public void serializeJson( Writer writer, String callback, boolean indent, boolean saveDocuments, boolean saveClusters, boolean saveOtherAttributes) throws IOException { final ObjectMapper mapper = new ObjectMapper(); mapper.getFactory().disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); mapper.enable(SerializationFeature.INDENT_OUTPUT); if (StringUtils.isNotBlank(callback)) { writer.write(callback + "("); } final Map<String, Object> attrs = prepareAttributesForSerialization(saveDocuments, saveClusters, saveOtherAttributes); mapper.writeValue(writer, attrs); if (StringUtils.isNotBlank(callback)) { writer.write(");"); } } /** * Prepares a temporary attributes map for serialization purposes. Includes only the requested * elements in the map. */ private Map<String, Object> prepareAttributesForSerialization( boolean saveDocuments, boolean saveClusters, boolean saveOtherAttributes) { final Map<String, Object> tempAttributes = Maps.newHashMap(); if (saveOtherAttributes) { tempAttributes.putAll(attributes); tempAttributes.remove(AttributeNames.DOCUMENTS); tempAttributes.remove(AttributeNames.CLUSTERS); } else { tempAttributes.put(AttributeNames.QUERY, attributes.get(AttributeNames.QUERY)); } if (saveDocuments) { tempAttributes.put(AttributeNames.DOCUMENTS, getDocuments()); } if (saveClusters) { tempAttributes.put(AttributeNames.CLUSTERS, getClusters()); } return tempAttributes; } }
public MutationState createTable(CreateTableStatement statement, byte[][] splits) throws SQLException { PTableType tableType = statement.getTableType(); boolean isView = tableType == PTableType.VIEW; if (isView && !statement.getProps().isEmpty()) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.VIEW_WITH_TABLE_CONFIG) .build() .buildException(); } connection.rollback(); boolean wasAutoCommit = connection.getAutoCommit(); try { connection.setAutoCommit(false); TableName tableNameNode = statement.getTableName(); String schemaName = tableNameNode.getSchemaName(); String tableName = tableNameNode.getTableName(); PrimaryKeyConstraint pkConstraint = statement.getPrimaryKeyConstraint(); String pkName = null; Set<String> pkColumns = Collections.<String>emptySet(); Iterator<String> pkColumnsIterator = Iterators.emptyIterator(); if (pkConstraint != null) { pkColumns = pkConstraint.getColumnNames(); pkColumnsIterator = pkColumns.iterator(); pkName = pkConstraint.getName(); } List<ColumnDef> colDefs = statement.getColumnDefs(); List<PColumn> columns = Lists.newArrayListWithExpectedSize(colDefs.size()); PreparedStatement colUpsert = connection.prepareStatement(INSERT_COLUMN); int columnOrdinal = 0; Map<String, PName> familyNames = Maps.newLinkedHashMap(); boolean isPK = false; for (ColumnDef colDef : colDefs) { if (colDef.isPK()) { if (isPK) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.PRIMARY_KEY_ALREADY_EXISTS) .setColumnName(colDef.getColumnDefName().getColumnName().getName()) .build() .buildException(); } isPK = true; } PColumn column = newColumn(columnOrdinal++, colDef, pkConstraint); if (SchemaUtil.isPKColumn(column)) { // TODO: remove this constraint? if (!pkColumns.isEmpty() && !column.getName().getString().equals(pkColumnsIterator.next())) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.PRIMARY_KEY_OUT_OF_ORDER) .setSchemaName(schemaName) .setTableName(tableName) .setColumnName(column.getName().getString()) .build() .buildException(); } } columns.add(column); if (colDef.getDataType() == PDataType.BINARY && colDefs.size() > 1) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.BINARY_IN_ROW_KEY) .setSchemaName(schemaName) .setTableName(tableName) .setColumnName(column.getName().getString()) .build() .buildException(); } if (column.getFamilyName() != null) { familyNames.put(column.getFamilyName().getString(), column.getFamilyName()); } } if (!isPK && pkColumns.isEmpty()) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.PRIMARY_KEY_MISSING) .setSchemaName(schemaName) .setTableName(tableName) .build() .buildException(); } List<Pair<byte[], Map<String, Object>>> familyPropList = Lists.newArrayListWithExpectedSize(familyNames.size()); Map<String, Object> commonFamilyProps = Collections.emptyMap(); Map<String, Object> tableProps = Collections.emptyMap(); if (!statement.getProps().isEmpty()) { if (statement.isView()) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.VIEW_WITH_PROPERTIES) .build() .buildException(); } for (String familyName : statement.getProps().keySet()) { if (!familyName.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY)) { if (familyNames.get(familyName) == null) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.PROPERTIES_FOR_FAMILY) .setFamilyName(familyName) .build() .buildException(); } } } commonFamilyProps = Maps.newHashMapWithExpectedSize(statement.getProps().size()); tableProps = Maps.newHashMapWithExpectedSize(statement.getProps().size()); Collection<Pair<String, Object>> props = statement.getProps().get(QueryConstants.ALL_FAMILY_PROPERTIES_KEY); // Somewhat hacky way of determining if property is for HColumnDescriptor or // HTableDescriptor HColumnDescriptor defaultDescriptor = new HColumnDescriptor(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES); for (Pair<String, Object> prop : props) { if (defaultDescriptor.getValue(prop.getFirst()) != null) { commonFamilyProps.put(prop.getFirst(), prop.getSecond()); } else { tableProps.put(prop.getFirst(), prop.getSecond()); } } } for (PName familyName : familyNames.values()) { Collection<Pair<String, Object>> props = statement.getProps().get(familyName.getString()); if (props.isEmpty()) { familyPropList.add( new Pair<byte[], Map<String, Object>>(familyName.getBytes(), commonFamilyProps)); } else { Map<String, Object> combinedFamilyProps = Maps.newHashMapWithExpectedSize(props.size() + commonFamilyProps.size()); combinedFamilyProps.putAll(commonFamilyProps); for (Pair<String, Object> prop : props) { combinedFamilyProps.put(prop.getFirst(), prop.getSecond()); } familyPropList.add( new Pair<byte[], Map<String, Object>>(familyName.getBytes(), combinedFamilyProps)); } } // Bootstrapping for our SYSTEM.TABLE that creates itself before it exists if (tableType == PTableType.SYSTEM) { PTable table = new PTableImpl( new PNameImpl(tableName), tableType, MetaDataProtocol.MIN_TABLE_TIMESTAMP, 0, QueryConstants.SYSTEM_TABLE_PK_NAME, null, columns); connection.addTable(schemaName, table); } for (PColumn column : columns) { addColumnMutation(schemaName, tableName, column, colUpsert); } Integer saltBucketNum = (Integer) tableProps.remove(PhoenixDatabaseMetaData.SALT_BUCKETS); if (saltBucketNum != null && (saltBucketNum <= 0 || saltBucketNum > SaltingUtil.MAX_BUCKET_NUM)) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.INVALID_BUCKET_NUM) .build() .buildException(); } PreparedStatement tableUpsert = connection.prepareStatement(CREATE_TABLE); tableUpsert.setString(1, schemaName); tableUpsert.setString(2, tableName); tableUpsert.setString(3, tableType.getSerializedValue()); tableUpsert.setInt(4, 0); tableUpsert.setInt(5, columnOrdinal); if (saltBucketNum != null) { tableUpsert.setInt(6, saltBucketNum); } else { tableUpsert.setNull(6, Types.INTEGER); } tableUpsert.setString(7, pkName); tableUpsert.execute(); final List<Mutation> tableMetaData = connection.getMutationState().toMutations(); connection.rollback(); MetaDataMutationResult result = connection .getQueryServices() .createTable(tableMetaData, isView, tableProps, familyPropList, splits); MutationCode code = result.getMutationCode(); switch (code) { case TABLE_ALREADY_EXISTS: connection.addTable(schemaName, result.getTable()); if (!statement.ifNotExists()) { throw new TableAlreadyExistsException(schemaName, tableName); } break; case NEWER_TABLE_FOUND: // TODO: add table if in result? throw new NewerTableAlreadyExistsException(schemaName, tableName); case UNALLOWED_TABLE_MUTATION: throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_MUTATE_TABLE) .setSchemaName(schemaName) .setTableName(tableName) .build() .buildException(); default: PTable table = new PTableImpl( new PNameImpl(tableName), tableType, result.getMutationTime(), 0, pkName, saltBucketNum, columns); connection.addTable(schemaName, table); if (tableType == PTableType.USER) { connection.setAutoCommit(true); // Delete everything in the column. You'll still be able to do queries at earlier // timestamps Long scn = connection.getSCN(); long ts = (scn == null ? result.getMutationTime() : scn); PSchema schema = new PSchemaImpl( schemaName, ImmutableMap.<String, PTable>of(table.getName().getString(), table)); TableRef tableRef = new TableRef(null, table, schema, ts); byte[] emptyCF = SchemaUtil.getEmptyColumnFamily(table.getColumnFamilies()); MutationPlan plan = new PostDDLCompiler(connection).compile(tableRef, emptyCF, null, ts); return connection.getQueryServices().updateData(plan); } break; } return new MutationState(0, connection); } finally { connection.setAutoCommit(wasAutoCommit); } }
@Test public void should_return_null_if_Map_is_null() { assertNull(Maps.format(null)); }
@Test public void should_return_empty_braces_if_Map_is_empty() { assertEquals(Maps.format(new HashMap<String, String>()), "{}"); }
public OptiqSchema(OptiqSchema parent, final Schema schema, String name) { this.parent = parent; this.schema = schema; this.name = name; assert (parent == null) == (this instanceof OptiqRootSchema); //noinspection unchecked this.compositeTableMap = CompositeMap.of( Maps.transformValues( tableMap, new com.google.common.base.Function<TableEntry, Table>() { public Table apply(TableEntry input) { return input.getTable(); } }), Maps.transformValues( Multimaps.filterEntries( functionMap, new Predicate<Map.Entry<String, FunctionEntry>>() { public boolean apply(Map.Entry<String, FunctionEntry> entry) { final Function function = entry.getValue().getFunction(); return function instanceof TableMacro && function.getParameters().isEmpty(); } }) .asMap(), new com.google.common.base.Function<Collection<FunctionEntry>, Table>() { public Table apply(Collection<FunctionEntry> input) { // At most one function with zero parameters. final TableMacro tableMacro = (TableMacro) input.iterator().next().getFunction(); return tableMacro.apply(ImmutableList.of()); } }), Compatible.INSTANCE.asMap( schema.getTableNames(), new com.google.common.base.Function<String, Table>() { public Table apply(String input) { return schema.getTable(input); } })); // TODO: include schema's functions in this map. this.compositeFunctionMap = Multimaps.transformValues( functionMap, new com.google.common.base.Function<FunctionEntry, Function>() { public Function apply(FunctionEntry input) { return input.getFunction(); } }); //noinspection unchecked this.compositeSubSchemaMap = CompositeMap.of( subSchemaMap, Compatible.INSTANCE.asMap( schema.getSubSchemaNames(), new com.google.common.base.Function<String, OptiqSchema>() { public OptiqSchema apply(String name) { return add(name, schema.getSubSchema(name)); } })); }
/** * Parse the given array of arguments. * * <p>Empty arguments are removed from the list of arguments. * * @param args an array with arguments * @param expectedValueFlags a set containing all value flags (pass null to disable value flag * parsing) * @param allowHangingFlag true if hanging flags are allowed * @param namespace the locals, null to create empty one * @throws CommandException thrown on a parsing error */ public CommandContext( String[] args, Set<Character> expectedValueFlags, boolean allowHangingFlag, Namespace namespace) throws CommandException { if (expectedValueFlags == null) { expectedValueFlags = Collections.emptySet(); } originalArgs = args; command = args[0]; this.namespace = namespace != null ? namespace : new Namespace(); boolean isHanging = false; SuggestionContext suggestionContext = SuggestionContext.hangingValue(); // Eliminate empty args and combine multiword args first List<Integer> argIndexList = new ArrayList<Integer>(args.length); List<String> argList = new ArrayList<String>(args.length); for (int i = 1; i < args.length; ++i) { isHanging = false; String arg = args[i]; if (arg.isEmpty()) { isHanging = true; continue; } argIndexList.add(i); switch (arg.charAt(0)) { case '\'': case '"': final StringBuilder build = new StringBuilder(); final char quotedChar = arg.charAt(0); int endIndex; for (endIndex = i; endIndex < args.length; ++endIndex) { final String arg2 = args[endIndex]; if (arg2.charAt(arg2.length() - 1) == quotedChar && arg2.length() > 1) { if (endIndex != i) build.append(' '); build.append(arg2.substring(endIndex == i ? 1 : 0, arg2.length() - 1)); break; } else if (endIndex == i) { build.append(arg2.substring(1)); } else { build.append(' ').append(arg2); } } if (endIndex < args.length) { arg = build.toString(); i = endIndex; } // In case there is an empty quoted string if (arg.isEmpty()) { continue; } // else raise exception about hanging quotes? } argList.add(arg); } // Then flags List<Integer> originalArgIndices = Lists.newArrayListWithCapacity(argIndexList.size()); List<String> parsedArgs = Lists.newArrayListWithCapacity(argList.size()); Map<Character, String> valueFlags = Maps.newHashMap(); List<Character> booleanFlags = Lists.newArrayList(); for (int nextArg = 0; nextArg < argList.size(); ) { // Fetch argument String arg = argList.get(nextArg++); suggestionContext = SuggestionContext.hangingValue(); // Not a flag? if (arg.charAt(0) != '-' || arg.length() == 1 || !arg.matches("^-[a-zA-Z\\?]+$")) { if (!isHanging) { suggestionContext = SuggestionContext.lastValue(); } originalArgIndices.add(argIndexList.get(nextArg - 1)); parsedArgs.add(arg); continue; } // Handle flag parsing terminator -- if (arg.equals("--")) { while (nextArg < argList.size()) { originalArgIndices.add(argIndexList.get(nextArg)); parsedArgs.add(argList.get(nextArg++)); } break; } // Go through the flag characters for (int i = 1; i < arg.length(); ++i) { char flagName = arg.charAt(i); if (expectedValueFlags.contains(flagName)) { if (valueFlags.containsKey(flagName)) { throw new CommandException("Value flag '" + flagName + "' already given"); } if (nextArg >= argList.size()) { if (allowHangingFlag) { suggestionContext = SuggestionContext.flag(flagName); break; } else { throw new CommandException("No value specified for the '-" + flagName + "' flag."); } } // If it is a value flag, read another argument and add it valueFlags.put(flagName, argList.get(nextArg++)); if (!isHanging) { suggestionContext = SuggestionContext.flag(flagName); } } else { booleanFlags.add(flagName); } } } ImmutableMap.Builder<Character, String> allFlagsBuilder = new ImmutableMap.Builder<Character, String>().putAll(valueFlags); for (Character flag : booleanFlags) { allFlagsBuilder.put(flag, "true"); } this.parsedArgs = ImmutableList.copyOf(parsedArgs); this.originalArgIndices = ImmutableList.copyOf(originalArgIndices); this.booleanFlags = ImmutableSet.copyOf(booleanFlags); this.valueFlags = ImmutableMap.copyOf(valueFlags); this.allFlags = allFlagsBuilder.build(); this.suggestionContext = suggestionContext; }
private void init( PName name, PTableType type, long timeStamp, long sequenceNumber, String pkName, List<PColumn> columns, PTableStats stats) { this.name = name; this.type = type; this.timeStamp = timeStamp; this.sequenceNumber = sequenceNumber; this.columnsByName = ArrayListMultimap.create(columns.size(), 1); this.pkName = pkName; List<PColumn> pkColumns = Lists.newArrayListWithExpectedSize(columns.size() - 1); PColumn[] allColumns = new PColumn[columns.size()]; RowKeySchemaBuilder builder = new RowKeySchemaBuilder(); for (int i = 0; i < allColumns.length; i++) { PColumn column = columns.get(i); allColumns[column.getPosition()] = column; PName familyName = column.getFamilyName(); if (familyName == null) { pkColumns.add(column); builder.addField(column); } columnsByName.put(column.getName().getString(), column); } this.pkColumns = ImmutableList.copyOf(pkColumns); this.rowKeySchema = builder.setMinNullable(pkColumns.size()).build(); this.allColumns = ImmutableList.copyOf(allColumns); // Two pass so that column order in column families matches overall column order // and to ensure that column family order is constant int maxExpectedSize = allColumns.length - pkColumns.size(); // Maintain iteration order so that column families are ordered as they are listed Map<PName, List<PColumn>> familyMap = Maps.newLinkedHashMap(); for (PColumn column : allColumns) { PName familyName = column.getFamilyName(); if (familyName != null) { List<PColumn> columnsInFamily = familyMap.get(familyName); if (columnsInFamily == null) { columnsInFamily = Lists.newArrayListWithExpectedSize(maxExpectedSize); familyMap.put(familyName, columnsInFamily); } columnsInFamily.add(column); } } Iterator<Map.Entry<PName, List<PColumn>>> iterator = familyMap.entrySet().iterator(); PColumnFamily[] families = new PColumnFamily[familyMap.size()]; ImmutableMap.Builder<String, PColumnFamily> familyByString = ImmutableMap.builder(); ImmutableSortedMap.Builder<byte[], PColumnFamily> familyByBytes = ImmutableSortedMap.orderedBy(Bytes.BYTES_COMPARATOR); for (int i = 0; i < families.length; i++) { Map.Entry<PName, List<PColumn>> entry = iterator.next(); PColumnFamily family = new PColumnFamilyImpl(entry.getKey(), entry.getValue()); families[i] = family; familyByString.put(family.getName().getString(), family); familyByBytes.put(family.getName().getBytes(), family); } this.families = ImmutableList.copyOf(families); this.familyByBytes = familyByBytes.build(); this.familyByString = familyByString.build(); this.stats = stats; }
public class FunctionUtil { private FunctionUtil() {} public static FieldValue evaluate( Apply apply, List<FieldValue> values, EvaluationContext context) { String name = apply.getFunction(); Function function = getFunction(name); if (function == null) { DefineFunction defineFunction = context.resolveFunction(name); if (defineFunction == null) { throw new UnsupportedFeatureException(apply); } return evaluate(defineFunction, values, context); } return function.evaluate(values); } public static FieldValue evaluate( DefineFunction defineFunction, List<FieldValue> values, EvaluationContext context) { List<ParameterField> parameterFields = defineFunction.getParameterFields(); if (parameterFields.size() < 1) { throw new InvalidFeatureException(defineFunction); } // End if if (parameterFields.size() != values.size()) { throw new EvaluationException(); } FunctionEvaluationContext functionContext = new FunctionEvaluationContext(context); for (int i = 0; i < parameterFields.size(); i++) { ParameterField parameterField = parameterFields.get(i); FieldValue value = FieldValueUtil.refine(parameterField, values.get(i)); functionContext.declare(parameterField.getName(), value); } Expression expression = defineFunction.getExpression(); if (expression == null) { throw new InvalidFeatureException(defineFunction); } FieldValue result = ExpressionUtil.evaluate(expression, functionContext); return FieldValueUtil.refine(defineFunction.getDataType(), defineFunction.getOptype(), result); } public static Function getFunction(String name) { return FunctionUtil.functions.get(name); } public static void putFunction(String name, Function function) { FunctionUtil.functions.put(name, function); } private static void checkArguments(List<FieldValue> values, int size) { checkArguments(values, size, false); } private static void checkArguments(List<FieldValue> values, int size, boolean allowNulls) { boolean success = (values.size() == size) && (allowNulls ? true : !values.contains(null)); if (!success) { throw new EvaluationException(); } } private static void checkVariableArguments(List<FieldValue> values, int size) { checkVariableArguments(values, size, false); } private static void checkVariableArguments( List<FieldValue> values, int size, boolean allowNulls) { boolean success = (values.size() >= size) && (allowNulls ? true : !values.contains(null)); if (!success) { throw new EvaluationException(); } } private static Number cast(DataType dataType, Number number) { switch (dataType) { case INTEGER: if (number instanceof Integer) { return number; } return Integer.valueOf(number.intValue()); case FLOAT: if (number instanceof Float) { return number; } return Float.valueOf(number.floatValue()); case DOUBLE: if (number instanceof Double) { return number; } return Double.valueOf(number.doubleValue()); default: break; } throw new EvaluationException(); } private static DataType integerToDouble(DataType dataType) { switch (dataType) { case INTEGER: return DataType.DOUBLE; default: break; } return dataType; } private static final Map<String, Function> functions = Maps.newLinkedHashMap(); public interface Function { FieldValue evaluate(List<FieldValue> values); } public abstract static class ArithmeticFunction implements Function { public abstract Number evaluate(Number left, Number right); @Override public FieldValue evaluate(List<FieldValue> values) { if (values.size() != 2) { throw new EvaluationException(); } FieldValue left = values.get(0); FieldValue right = values.get(1); // "If one of the input fields of a simple arithmetic function is a missing value, the result // evaluates to missing value" if (left == null || right == null) { return null; } DataType dataType = TypeUtil.getResultDataType(left.getDataType(), right.getDataType()); Number result; try { result = evaluate(left.asNumber(), right.asNumber()); } catch (ArithmeticException ae) { throw new InvalidResultException(null); } return FieldValueUtil.create(cast(dataType, result)); } } static { putFunction( "+", new ArithmeticFunction() { @Override public Double evaluate(Number left, Number right) { return Double.valueOf(left.doubleValue() + right.doubleValue()); } }); putFunction( "-", new ArithmeticFunction() { @Override public Double evaluate(Number left, Number right) { return Double.valueOf(left.doubleValue() - right.doubleValue()); } }); putFunction( "*", new ArithmeticFunction() { @Override public Double evaluate(Number left, Number right) { return Double.valueOf(left.doubleValue() * right.doubleValue()); } }); putFunction( "/", new ArithmeticFunction() { @Override public Number evaluate(Number left, Number right) { if (left instanceof Integer && right instanceof Integer) { return Integer.valueOf(left.intValue() / right.intValue()); } return Double.valueOf(left.doubleValue() / right.doubleValue()); } }); } public abstract static class AggregateFunction implements Function { public abstract StorelessUnivariateStatistic createStatistic(); public DataType getResultType(DataType dataType) { return dataType; } @Override public FieldValue evaluate(List<FieldValue> values) { StorelessUnivariateStatistic statistic = createStatistic(); DataType dataType = null; for (FieldValue value : values) { // "Missing values in the input to an aggregate function are simply ignored" if (value == null) { continue; } statistic.increment((value.asNumber()).doubleValue()); if (dataType != null) { dataType = TypeUtil.getResultDataType(dataType, value.getDataType()); } else { dataType = value.getDataType(); } } if (statistic.getN() == 0) { throw new MissingResultException(null); } Object result = cast(getResultType(dataType), statistic.getResult()); return FieldValueUtil.create(result); } } static { putFunction( "min", new AggregateFunction() { @Override public Min createStatistic() { return new Min(); } }); putFunction( "max", new AggregateFunction() { @Override public Max createStatistic() { return new Max(); } }); putFunction( "avg", new AggregateFunction() { @Override public Mean createStatistic() { return new Mean(); } @Override public DataType getResultType(DataType dataType) { return integerToDouble(dataType); } }); putFunction( "sum", new AggregateFunction() { @Override public Sum createStatistic() { return new Sum(); } }); putFunction( "product", new AggregateFunction() { @Override public Product createStatistic() { return new Product(); } }); } public abstract static class MathFunction implements Function { public abstract Double evaluate(Number value); public DataType getResultType(DataType dataType) { return dataType; } @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 1); FieldValue value = values.get(0); Number result = cast(getResultType(value.getDataType()), evaluate(value.asNumber())); return FieldValueUtil.create(result); } } public abstract static class FpMathFunction extends MathFunction { @Override public DataType getResultType(DataType dataType) { return integerToDouble(dataType); } } static { putFunction( "log10", new FpMathFunction() { @Override public Double evaluate(Number value) { return Math.log10(value.doubleValue()); } }); putFunction( "ln", new FpMathFunction() { @Override public Double evaluate(Number value) { return Math.log(value.doubleValue()); } }); putFunction( "exp", new FpMathFunction() { @Override public Double evaluate(Number value) { return Math.exp(value.doubleValue()); } }); putFunction( "sqrt", new FpMathFunction() { @Override public Double evaluate(Number value) { return Math.sqrt(value.doubleValue()); } }); putFunction( "abs", new MathFunction() { @Override public Double evaluate(Number value) { return Math.abs(value.doubleValue()); } }); putFunction( "pow", new Function() { @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 2); FieldValue left = values.get(0); FieldValue right = values.get(1); DataType dataType = TypeUtil.getResultDataType(left.getDataType(), right.getDataType()); Double result = Math.pow((left.asNumber()).doubleValue(), (right.asNumber()).doubleValue()); return FieldValueUtil.create(cast(dataType, result)); } }); putFunction( "threshold", new Function() { @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 2); FieldValue left = values.get(0); FieldValue right = values.get(1); DataType dataType = TypeUtil.getResultDataType(left.getDataType(), right.getDataType()); Integer result = ((left.asNumber()).doubleValue() > (right.asNumber()).doubleValue()) ? 1 : 0; return FieldValueUtil.create(cast(dataType, result)); } }); putFunction( "floor", new MathFunction() { @Override public Double evaluate(Number number) { return Math.floor(number.doubleValue()); } }); putFunction( "ceil", new MathFunction() { @Override public Double evaluate(Number number) { return Math.ceil(number.doubleValue()); } }); putFunction( "round", new MathFunction() { @Override public Double evaluate(Number number) { return (double) Math.round(number.doubleValue()); } }); } public abstract static class ValueFunction implements Function { public abstract Boolean evaluate(FieldValue value); @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 1, true); FieldValue value = values.get(0); Boolean result = evaluate(value); return FieldValueUtil.create(result); } } static { putFunction( "isMissing", new ValueFunction() { @Override public Boolean evaluate(FieldValue value) { return Boolean.valueOf(value == null); } }); putFunction( "isNotMissing", new ValueFunction() { @Override public Boolean evaluate(FieldValue value) { return Boolean.valueOf(value != null); } }); } public abstract static class EqualityFunction implements Function { public abstract Boolean evaluate(boolean equals); @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 2); FieldValue left = values.get(0); FieldValue right = values.get(1); Boolean result = evaluate((left).equalsValue(right)); return FieldValueUtil.create(result); } } static { putFunction( "equal", new EqualityFunction() { @Override public Boolean evaluate(boolean equals) { return Boolean.valueOf(equals); } }); putFunction( "notEqual", new EqualityFunction() { @Override public Boolean evaluate(boolean equals) { return Boolean.valueOf(!equals); } }); } public abstract static class ComparisonFunction implements Function { public abstract Boolean evaluate(int order); @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 2); FieldValue left = values.get(0); FieldValue right = values.get(1); Boolean result = evaluate((left).compareToValue(right)); return FieldValueUtil.create(result); } } static { putFunction( "lessThan", new ComparisonFunction() { @Override public Boolean evaluate(int order) { return Boolean.valueOf(order < 0); } }); putFunction( "lessOrEqual", new ComparisonFunction() { @Override public Boolean evaluate(int order) { return Boolean.valueOf(order <= 0); } }); putFunction( "greaterThan", new ComparisonFunction() { @Override public Boolean evaluate(int order) { return Boolean.valueOf(order > 0); } }); putFunction( "greaterOrEqual", new ComparisonFunction() { @Override public Boolean evaluate(int order) { return Boolean.valueOf(order >= 0); } }); } public abstract static class BinaryBooleanFunction implements Function { public abstract Boolean evaluate(Boolean left, Boolean right); @Override public FieldValue evaluate(List<FieldValue> values) { checkVariableArguments(values, 2); Boolean result = (values.get(0)).asBoolean(); for (int i = 1; i < values.size(); i++) { result = evaluate(result, (values.get(i)).asBoolean()); } return FieldValueUtil.create(result); } } static { putFunction( "and", new BinaryBooleanFunction() { @Override public Boolean evaluate(Boolean left, Boolean right) { return Boolean.valueOf(left.booleanValue() & right.booleanValue()); } }); putFunction( "or", new BinaryBooleanFunction() { @Override public Boolean evaluate(Boolean left, Boolean right) { return Boolean.valueOf(left.booleanValue() | right.booleanValue()); } }); } public abstract static class UnaryBooleanFunction implements Function { public abstract Boolean evaluate(Boolean value); @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 1); FieldValue value = values.get(0); Boolean result = evaluate(value.asBoolean()); return FieldValueUtil.create(result); } } static { putFunction( "not", new UnaryBooleanFunction() { @Override public Boolean evaluate(Boolean value) { return Boolean.valueOf(!value.booleanValue()); } }); } public abstract static class ValueListFunction implements Function { public abstract Boolean evaluate(FieldValue value, List<FieldValue> values); @Override public FieldValue evaluate(List<FieldValue> values) { checkVariableArguments(values, 2); Boolean result = evaluate(values.get(0), values.subList(1, values.size())); return FieldValueUtil.create(result); } } static { putFunction( "isIn", new ValueListFunction() { @Override public Boolean evaluate(FieldValue value, List<FieldValue> values) { return value.equalsAnyValue(values); } }); putFunction( "isNotIn", new ValueListFunction() { @Override public Boolean evaluate(FieldValue value, List<FieldValue> values) { return !value.equalsAnyValue(values); } }); } static { putFunction( "if", new Function() { @Override public FieldValue evaluate(List<FieldValue> values) { if ((values.size() < 2 || values.size() > 3)) { throw new EvaluationException(); } FieldValue flag = values.get(0); if (flag == null) { throw new EvaluationException(); } // End if if (flag.asBoolean()) { FieldValue trueValue = values.get(1); // "The THEN part is required" if (trueValue == null) { throw new EvaluationException(); } return trueValue; } else { FieldValue falseValue = (values.size() > 2 ? values.get(2) : null); // "The ELSE part is optional. If the ELSE part is absent then a missing value is // returned" if (falseValue == null) { return null; } return falseValue; } } }); } public abstract static class StringFunction implements Function { public abstract String evaluate(String value); @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 1); FieldValue value = values.get(0); String result = evaluate(value.asString()); return FieldValueUtil.create(result); } } static { putFunction( "uppercase", new StringFunction() { @Override public String evaluate(String value) { return value.toUpperCase(); } }); putFunction( "lowercase", new StringFunction() { @Override public String evaluate(String value) { return value.toLowerCase(); } }); putFunction( "substring", new Function() { @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 3); String string = (values.get(0)).asString(); int position = (values.get(1)).asInteger(); int length = (values.get(2)).asInteger(); // "The first character of a string is located at position 1 (not position 0)" if (position <= 0 || length < 0) { throw new EvaluationException(); } String result = string.substring(position - 1, (position + length) - 1); return FieldValueUtil.create(result); } }); putFunction( "trimBlanks", new StringFunction() { @Override public String evaluate(String value) { return value.trim(); } }); } static { putFunction( "formatNumber", new Function() { @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 2); FieldValue value = values.get(0); FieldValue pattern = values.get(1); String result; // According to the java.util.Formatter javadoc, Java formatting is more strict than C's // printf formatting. // For example, in Java, if a conversion is incompatible with a flag, an exception will // be thrown. In C's printf, inapplicable flags are silently ignored. try { result = String.format(pattern.asString(), value.asNumber()); } catch (IllegalFormatException ife) { throw ife; } return FieldValueUtil.create(result); } }); putFunction( "formatDatetime", new Function() { @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 2); FieldValue value = values.get(0); FieldValue pattern = values.get(1); String result; try { result = String.format( translatePattern(pattern.asString()), (value.asDateTime()).toDate()); } catch (IllegalFormatException ife) { throw ife; } return FieldValueUtil.create(result); } private String translatePattern(String pattern) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < pattern.length(); i++) { char c = pattern.charAt(i); sb.append(c); if (c == '%') { // Every %[conversion] has to become %1$t[conversion] // Here, "1$" denotes the first argument, and "t" denotes the prefix for date and // time conversion characters if (i < (pattern.length() - 1) && pattern.charAt(i + 1) != '%') { sb.append("1$t"); } } } return sb.toString(); } }); } static { putFunction( "dateDaysSinceYear", new Function() { @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 2); LocalDate instant = (values.get(0)).asLocalDate(); int year = (values.get(1)).asInteger(); DaysSinceDate period = new DaysSinceDate(year, instant); return FieldValueUtil.create(period.intValue()); } }); putFunction( "dateSecondsSinceMidnight", new Function() { @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 1); LocalTime instant = (values.get(0)).asLocalTime(); Seconds seconds = Seconds.seconds( instant.getHourOfDay() * 60 * 60 + instant.getMinuteOfHour() * 60 + instant.getSecondOfMinute()); SecondsSinceMidnight period = new SecondsSinceMidnight(seconds); return FieldValueUtil.create(period.intValue()); } }); putFunction( "dateSecondsSinceYear", new Function() { @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 2); LocalDateTime instant = (values.get(0)).asLocalDateTime(); int year = (values.get(1)).asInteger(); SecondsSinceDate period = new SecondsSinceDate(year, instant); return FieldValueUtil.create(period.intValue()); } }); } }
/** For JSON and XML serialization only. */ @JsonProperty("attributes") @SuppressWarnings("unused") private Map<String, Object> getOtherAttributes() { final Map<String, Object> otherAttributes = Maps.newHashMap(attributesView); return otherAttributes.isEmpty() ? null : otherAttributes; }