/* * Copyright 2014 Red Hat, Inc. and/or its affiliates. * * Licensed under the Eclipse Public License version 1.0, available at * http://www.eclipse.org/legal/epl-v10.html */ package org.jboss.windup.config; import java.util.Collections; import java.util.Deque; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; import javax.enterprise.inject.Vetoed; import org.jboss.windup.config.exception.IllegalTypeArgumentException; import org.jboss.windup.config.operation.Iteration; import org.jboss.windup.graph.model.WindupVertexFrame; /** * A variables stack - keeps few layers of "key"->[vertices] maps, one per rule execution level, {@link Iteration} and * {@link RuleSubset}. * * @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a> */ @Vetoed public class Variables { public static int SEARCH_ALL_LAYERS = Integer.MAX_VALUE; private final Deque<Map<String, Iterable<? extends WindupVertexFrame>>> deque = new LinkedList<>(); private Variables() { } /** * Get an instance of the {@link Variables} stack from the given {@link GraphRewrite} event context. */ public static Variables instance(GraphRewrite event) { Variables instance = (Variables) event.getRewriteContext().get(Variables.class); if (instance == null) { instance = new Variables(); event.getRewriteContext().put(Variables.class, instance); } return instance; } /** * Add new {@link Variables} layer on top of the stack. */ public void push() { Map<String, Iterable<? extends WindupVertexFrame>> newFrame = new HashMap<>(); deque.push(newFrame); } /** * Push the given {@link Variables} layer on top of the stack. */ public void push(Map<String, Iterable<? extends WindupVertexFrame>> frame) { deque.push(frame); } /** * Remove the top {@link Variables} layer from the the stack. */ public Map<String, Iterable<? extends WindupVertexFrame>> pop() { Map<String, Iterable<? extends WindupVertexFrame>> frame = deque.pop(); return frame; } /** * Get the top {@link Variables} layer from the stack. */ public Map<String, Iterable<? extends WindupVertexFrame>> peek() { return deque.peek(); } /** * Type-safe wrapper around setVariable which sets only one framed vertex. */ public void setSingletonVariable(String name, WindupVertexFrame frame) { setVariable(name, Collections.singletonList(frame)); } /** * Set a variable in the top variables layer to given "collection" of the vertex frames. Can't be reassigned - * throws on attempt to reassign. */ public void setVariable(String name, Iterable<? extends WindupVertexFrame> frames) { Map<String, Iterable<? extends WindupVertexFrame>> frame = peek(); if (!Iteration.DEFAULT_VARIABLE_LIST_STRING.equals(name) && findVariable(name) != null) { throw new IllegalArgumentException("Variable \"" + name + "\" has already been assigned and cannot be reassigned"); } frame.put(name, frames); } /** * Remove a variable in the top variables layer. */ public void removeVariable(String name) { Map<String, Iterable<? extends WindupVertexFrame>> frame = peek(); frame.remove(name); } /** * Wrapper around {@link #findVariable(String)} which gives only one framed vertex, and checks if there is 0 or 1; * throws otherwise. */ @SuppressWarnings("unchecked") public <T extends WindupVertexFrame> T findSingletonVariable(String name) { Iterable<? extends WindupVertexFrame> frames = findVariable(name); if (null == frames) { throw new IllegalStateException("Variable not found: \"" + name + "\""); } Iterator<? extends WindupVertexFrame> iterator = frames.iterator(); if (!iterator.hasNext()) { return null; } WindupVertexFrame obj = iterator.next(); if (iterator.hasNext()) { throw new IllegalStateException("More than one frame present " + "under presumed singleton variable: " + name); } return (T) obj; } /** * Type-safe wrapper around {@link #findVariable(String)} returns a unique {@link WindupVertexFrame}. * * @throws IllegalStateException If more than one frame was found. */ @SuppressWarnings("unchecked") public <FRAMETYPE extends WindupVertexFrame> FRAMETYPE findSingletonVariable(Class<FRAMETYPE> type, String name) { WindupVertexFrame frame = findSingletonVariable(name); if (type != null && !type.isAssignableFrom(frame.getClass())) { throw new IllegalTypeArgumentException(name, type, frame.getClass()); } return (FRAMETYPE) frame; } /** * Searches the variables layers, top to bottom, for given name, and returns if found; null otherwise. */ public Iterable<? extends WindupVertexFrame> findVariable(String name) { return findVariable(name, SEARCH_ALL_LAYERS); } /** * Searches the variables layers, top to bottom, for given name, and returns if found; null otherwise. * * If maxDepth is set to {@link Variables#SEARCH_ALL_LAYERS}, then search all layers. */ public Iterable<? extends WindupVertexFrame> findVariable(String name, int maxDepth) { int currentDepth = 0; Iterable<? extends WindupVertexFrame> result = null; for (Map<String, Iterable<? extends WindupVertexFrame>> frame : deque) { result = frame.get(name); if (result != null) { break; } currentDepth++; if (currentDepth >= maxDepth) break; } return result; } /** * Searches the variables layers, top to bottom, for the iterable having all of it's items of the given type. Return * null if not found. */ @SuppressWarnings("unchecked") public <T extends WindupVertexFrame> Iterable<T> findVariableOfType(Class<T> type) { for (Map<String, Iterable<? extends WindupVertexFrame>> topOfStack : deque) { for (Iterable<? extends WindupVertexFrame> frames : topOfStack.values()) { boolean empty = true; for (WindupVertexFrame frame : frames) { if (!type.isAssignableFrom(frame.getClass())) { break; } else { empty = false; } } // now we know all the frames are of the chosen type if (!empty) return (Iterable<T>) frames; } } return null; } @Override public String toString() { return "Variables [depth=" + deque.size() + "]"; } }