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

edu.internet2.middleware.grouper.grouperUi.serviceLogic.UiV2Visualization Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2018 Internet2
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package edu.internet2.middleware.grouper.grouperUi.serviceLogic;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;

import edu.internet2.middleware.grouper.Group;
import edu.internet2.middleware.grouper.GrouperSession;
import edu.internet2.middleware.grouper.Stem;
import edu.internet2.middleware.grouper.SubjectFinder;
import edu.internet2.middleware.grouper.app.graph.GraphEdge;
import edu.internet2.middleware.grouper.app.graph.GraphNode;
import edu.internet2.middleware.grouper.app.graph.RelationGraph;
import edu.internet2.middleware.grouper.app.grouperTypes.GrouperObjectTypesSettings;
import edu.internet2.middleware.grouper.app.visualization.StyleObjectType;
import edu.internet2.middleware.grouper.app.visualization.VisualSettings;
import edu.internet2.middleware.grouper.app.visualization.VisualStyleSet;
import edu.internet2.middleware.grouper.attr.AttributeDef;
import edu.internet2.middleware.grouper.cfg.GrouperConfig;
import edu.internet2.middleware.grouper.grouperUi.beans.api.GuiGroup;
import edu.internet2.middleware.grouper.grouperUi.beans.api.GuiStem;
import edu.internet2.middleware.grouper.grouperUi.beans.api.GuiSubject;
import edu.internet2.middleware.grouper.grouperUi.beans.json.GuiResponseJs;
import edu.internet2.middleware.grouper.grouperUi.beans.json.GuiScreenAction;
import edu.internet2.middleware.grouper.grouperUi.beans.json.GuiScreenAction.GuiMessageType;
import edu.internet2.middleware.grouper.grouperUi.beans.preferences.UiV2VisualizationPreference;
import edu.internet2.middleware.grouper.grouperUi.beans.ui.GroupContainer;
import edu.internet2.middleware.grouper.grouperUi.beans.ui.GrouperRequestContainer;
import edu.internet2.middleware.grouper.grouperUi.beans.ui.StemContainer;
import edu.internet2.middleware.grouper.grouperUi.beans.ui.SubjectContainer;
import edu.internet2.middleware.grouper.grouperUi.beans.ui.TextContainer;
import edu.internet2.middleware.grouper.grouperUi.beans.ui.VisualizationContainer;
import edu.internet2.middleware.grouper.internal.util.GrouperUuid;
import edu.internet2.middleware.grouper.misc.GrouperObjectSubjectWrapper;
import edu.internet2.middleware.grouper.ui.GrouperUiFilter;
import edu.internet2.middleware.grouper.ui.util.GrouperUiConfig;
import edu.internet2.middleware.grouper.ui.util.GrouperUiUserData;
import edu.internet2.middleware.grouper.ui.util.GrouperUiUtils;
import edu.internet2.middleware.grouper.ui.util.ProgressBean;
import edu.internet2.middleware.grouper.userData.GrouperUserDataApi;
import edu.internet2.middleware.grouper.util.GrouperCallable;
import edu.internet2.middleware.grouper.util.GrouperFuture;
import edu.internet2.middleware.grouper.util.GrouperUtil;
import edu.internet2.middleware.grouperClient.collections.MultiKey;
import edu.internet2.middleware.grouperClient.util.ExpirableCache;
import edu.internet2.middleware.subject.Source;
import edu.internet2.middleware.subject.Subject;
import edu.internet2.middleware.subject.SubjectNotUniqueException;
import edu.internet2.middleware.subject.provider.SourceManager;

/**
 * Operations to display a graph of object relationships
 */
public class UiV2Visualization {

  /**
   * Internal class used for json-lib serialization; needs to be public for
   * introspection to work
   */
  public class VisualizationGraph {
    private Map settings;
    private Map statistics;
    private Map> styles;
    private Map> fallbackStyles;

    public VisualizationGraph() {
      settings = new HashMap();
      statistics = new HashMap();
      styles = new HashMap>();
      fallbackStyles = new HashMap>();
    }

    protected void addSetting(String name, Object value) {
      this.settings.put(name, value);
    }

    protected void addStatistic(String name, String value) {
      this.statistics.put(name, value);
    }

    protected void addStyleProperty(String styleName, String propertyName, String propertyValue) {
      if (!styles.containsKey(styleName)) {
        styles.put(styleName, new HashMap());
      }
      styles.get(styleName).put(propertyName, propertyValue);
    }

    protected void addFallbackStyleProperty(String styleName, String propertyName, String propertyValue) {
      if (!fallbackStyles.containsKey(styleName)) {
        fallbackStyles.put(styleName, new HashMap());
      }
      fallbackStyles.get(styleName).put(propertyName, propertyValue);
    }

    public Map getSettings() {
      return settings;
    }

    public Map getStatistics() {
      return statistics;
    }

    public Map> getStyles() {
      return styles;
    }

    public Map> getFallbackStyles() {
      return fallbackStyles;
    }
  }

  public class TextGraph extends VisualizationGraph { //note need to be public for json-lib to parse them
    private Map nodes;
    private List sortedNodeIds;

    private TextGraph() {
      super();
      nodes = new HashMap();
      sortedNodeIds = new LinkedList();
    }

    private void addNode(String id, Node node) {
      this.nodes.put(id, node);
    }

    public Map getNodes() {
      return nodes;
    }

    public List getSortedNodeIds() {
      return sortedNodeIds;
    }

    public void addSortedNodeId(String id) {
      this.sortedNodeIds.add(id);
    }
  }

  /**
   * Internal class used for json-lib serialization; needs to be public for
   * introspection to work
   */
  public class D3Graph extends VisualizationGraph {
    private Map nodes;
    private List links;

    private D3Graph() {
      super();
      nodes = new HashMap();
      links = new LinkedList();
    }

    private void addNode(String id, Node node) {
      this.nodes.put(id, node);
    }

    private void addLink(D3Link link) {
      this.links.add(link);
    }

    public Map getNodes() {
      return nodes;
    }

    public List getLinks() {
      return links;
    }

  }

  /**
   * Internal class used for json-lib serialization; needs to be public for
   * introspection to work
   */
  public class Node {
    private String id;
    private String name;
    private String displayExtension;
    private String description;
    private String type;
    private String baseType;
    private String linkType;
    private long allMemberCount = 0;
    private long directMemberCount = 0;
    private List objectTypes;
    private String compositeLeftFactorId;
    private String compositeRightFactorId;

    private Node(String id, String name, String displayExtension, String description, String type,
                 String baseType, String linkType, long allMemberCount, long directMemberCount, List objectTypes) {
      this.id = id;
      this.name = name;
      this.displayExtension = displayExtension;
      this.description = description;
      this.type = type;
      this.baseType = baseType;
      this.linkType = linkType;
      this.allMemberCount = allMemberCount;
      this.directMemberCount = directMemberCount;
      this.objectTypes = objectTypes;
    }

    public String getId() {
      return id;
    }

    public String getName() {
      return name;
    }

    public String getDisplayExtension() {
      return displayExtension;
    }

    public String getDescription() {
      return description;
    }

    public String getType() {
      return type;
    }

    public String getBaseType() {
      return baseType;
    }

    public String getLinkType() {
      return linkType;
    }

    public long getAllMemberCount() {
      return allMemberCount;
    }

    public List getObjectTypes() {
      return objectTypes;
    }

    // helper method to add a custom object type with initialization; GraphNode objectTypes are null until set
    protected void addObjectTypeName(String name) {
      if (objectTypes == null) {
        objectTypes = new LinkedList();
      }
      objectTypes.add(name);
    }


    public long getDirectMemberCount() {
      return directMemberCount;
    }

    public String getCompositeLeftFactorId() {
      return compositeLeftFactorId;
    }

    protected void setCompositeLeftFactorId(String compositeLeftFactorId) {
      this.compositeLeftFactorId = compositeLeftFactorId;
    }

    public String getCompositeRightFactorId() {
      return compositeRightFactorId;
    }

    protected void setCompositeRightFactorId(String compositeRightFactorId) {
      this.compositeRightFactorId = compositeRightFactorId;
    }

  }

  /**
   * Internal class used for json-lib serialization; needs to be public for
   * introspection to work
   */
  public class TextNode extends Node {
    private long indent;
    private List parentNodeIds;
    private List childNodeIds;

    private TextNode(String id, String name, String displayExtension, String description, String type,
                     String baseType, String linkType, long allMemberCount, long directMemberCount,
                     List objectTypes, long indent) {
      super(id, name, displayExtension, description, type, baseType, linkType, allMemberCount, directMemberCount, objectTypes);
      this.indent = indent;
      parentNodeIds = new LinkedList();
      childNodeIds = new LinkedList();
    }

    public long getIndent() {
      return indent;
    }

    public List getParentNodeIds() {
      return parentNodeIds;
    }

    private void addParentNodeId(String id) {
      this.parentNodeIds.add(id);
    }

    public List getChildNodeIds() {
      return childNodeIds;
    }

    private void addChildNodeId(String id) {
      this.childNodeIds.add(id);
    }
  }

  /**
   * Internal class used for json-lib serialization; needs to be public for
   * introspection to work
   */
  public class D3Link {
    private String source;
    private String target;
    private String type;

    public D3Link(String source, String target, String type) {
      this.source = source;
      this.target = target;
      this.type = type;
    }

    public String getSource() {
      return source;
    }

    private void setSource(String source) {
      this.source = source;
    }

    public String getTarget() {
      return target;
    }

    private void setTarget(String target) {
      this.target = target;
    }

    public String getType() {
      return type;
    }

    private void setType(String type) {
      this.type = type;
    }
  }

  /* **** End of json-lib serialization classes **** */

  /**
   * Show the visualization form and graph, starting with a {@link Group} object
   *
   * @param request
   * @param response
   */
  public void groupView(HttpServletRequest request, HttpServletResponse response) {

    GrouperRequestContainer grouperRequestContainer = GrouperRequestContainer.retrieveFromRequestOrCreate();
    VisualizationContainer visualizationContainer = grouperRequestContainer.getVisualizationContainer();

    final Subject loggedInSubject = GrouperUiFilter.retrieveSubjectLoggedIn();
    GrouperSession grouperSession = null;

    try {

      grouperSession = GrouperSession.start(loggedInSubject);
      visualizationHelper(loggedInSubject, request, visualizationContainer, false);

      Group group = (Group) visualizationContainer.getGrouperObject();
      GroupContainer groupContainer = grouperRequestContainer.getGroupContainer();
      groupContainer.setGuiGroup(new GuiGroup(group));

      GuiResponseJs guiResponseJs = GuiResponseJs.retrieveGuiResponseJs();

      guiResponseJs.addAction(GuiScreenAction.newInnerHtmlFromJsp("#grouperMainContentDivId",
              "/WEB-INF/grouperUi2/group/groupVisualization.jsp"));

      if (GrouperUiUtils.isMenuRefreshOnView()) {
        guiResponseJs.addAction(GuiScreenAction.newScript("openFolderTreePathToObject(" + GrouperUiUtils.pathArrayToCurrentObject(grouperSession, group) + ")"));
      }
    } finally {
      GrouperSession.stopQuietly(grouperSession);
    }
  }

  /**
   * Show the visualization form and graph, starting with a {@link Stem} object
   *
   * @param request
   * @param response
   */
  public void stemView(HttpServletRequest request, HttpServletResponse response) {

    GrouperRequestContainer grouperRequestContainer = GrouperRequestContainer.retrieveFromRequestOrCreate();
    VisualizationContainer visualizationContainer = grouperRequestContainer.getVisualizationContainer();

    final Subject loggedInSubject = GrouperUiFilter.retrieveSubjectLoggedIn();
    GrouperSession grouperSession = null;

    try {
      grouperSession = GrouperSession.start(loggedInSubject);

      visualizationHelper(loggedInSubject, request, visualizationContainer, false);

      Stem stem = (Stem) visualizationContainer.getGrouperObject();
      StemContainer stemContainer = grouperRequestContainer.getStemContainer();
      stemContainer.setGuiStem(new GuiStem(stem));

      GuiResponseJs guiResponseJs = GuiResponseJs.retrieveGuiResponseJs();

      guiResponseJs.addAction(GuiScreenAction.newInnerHtmlFromJsp("#grouperMainContentDivId",
              "/WEB-INF/grouperUi2/stem/stemVisualization.jsp"));

      if (GrouperUiUtils.isMenuRefreshOnView()) {
        guiResponseJs.addAction(GuiScreenAction.newScript("openFolderTreePathToObject(" + GrouperUiUtils.pathArrayToCurrentObject(grouperSession, stem) + ")"));
      }
    } finally {
      GrouperSession.stopQuietly(grouperSession);
    }

  }

  /**
   * Show the visualization form and graph, starting with a {@link GrouperObjectSubjectWrapper} object
   *
   * @param request
   * @param response
   */
  public void subjectView(HttpServletRequest request, HttpServletResponse response) {

    GrouperRequestContainer grouperRequestContainer = GrouperRequestContainer.retrieveFromRequestOrCreate();
    VisualizationContainer visualizationContainer = grouperRequestContainer.getVisualizationContainer();

    final Subject loggedInSubject = GrouperUiFilter.retrieveSubjectLoggedIn();
    GrouperSession grouperSession = null;

    try {
      grouperSession = GrouperSession.start(loggedInSubject);

      visualizationHelper(loggedInSubject, request, visualizationContainer, false);

      Subject subject = UiV2Subject.retrieveSubjectHelper(request, true);

      if (subject == null) {
        return;
      }

      GrouperObjectSubjectWrapper subjectWrapped = (GrouperObjectSubjectWrapper) visualizationContainer.getGrouperObject();
      subject = subjectWrapped.getSubject();
      SubjectContainer subjectContainer = grouperRequestContainer.getSubjectContainer();
      subjectContainer.setGuiSubject(new GuiSubject(subject));

      GuiResponseJs guiResponseJs = GuiResponseJs.retrieveGuiResponseJs();

      guiResponseJs.addAction(GuiScreenAction.newInnerHtmlFromJsp("#grouperMainContentDivId",
              "/WEB-INF/grouperUi2/subject/subjectVisualization.jsp"));
    } finally {
      GrouperSession.stopQuietly(grouperSession);
    }
  }

  /** logger */
  protected static final Log LOG = edu.internet2.middleware.grouper.util.GrouperUtil.getLog(UiV2Visualization.class);

  /**
   * Internal ajax call to get the graph based on settings. Puts json into the visualizationObject DOM element
   *
   * @param request
   * @param response
   */
  public void buildGraph(HttpServletRequest request, HttpServletResponse response) {
    GrouperRequestContainer grouperRequestContainer = GrouperRequestContainer.retrieveFromRequestOrCreate();
    VisualizationContainer visualizationContainer = grouperRequestContainer.getVisualizationContainer();

    final Subject loggedInSubject = GrouperUiFilter.retrieveSubjectLoggedIn();
    GrouperSession grouperSession = null;

    try {
      grouperSession = GrouperSession.start(loggedInSubject);
      visualizationHelper(loggedInSubject, request, visualizationContainer, true);

      GuiResponseJs guiResponseJs = GuiResponseJs.retrieveGuiResponseJs();

      String searchSubjectString = request.getParameter("visualizationAddMemberComboName");
      
      Subject isMemberSubject = null;
      if (StringUtils.isNotBlank(searchSubjectString)) {
        
        if (searchSubjectString != null && searchSubjectString.contains("||")) {
          String sourceId = GrouperUtil.prefixOrSuffix(searchSubjectString, "||", true);
          String subjectId = GrouperUtil.prefixOrSuffix(searchSubjectString, "||", false);
          isMemberSubject =  SubjectFinder.findByIdOrIdentifierAndSource(subjectId, sourceId, false);
        } else {
          try {
            isMemberSubject = SubjectFinder.findByIdOrIdentifier(searchSubjectString, false);
          } catch (SubjectNotUniqueException snue) {
            //ignore
          }
            
        }

        if (isMemberSubject == null) {
          guiResponseJs.addAction(GuiScreenAction.newMessage(GuiMessageType.error, 
              TextContainer.retrieveFromRequest().getText().get("groupAddMemberCantFindSubject")));
          return;
        }    
      }

      RelationGraph relationGraph = new RelationGraph()
              .assignStartObject(visualizationContainer.getGrouperObject())
              .assignParentLevels(visualizationContainer.getDrawNumParentsLevels())
              .assignChildLevels(visualizationContainer.getDrawNumChildrenLevels())
              .assignShowStems(visualizationContainer.isDrawShowStems())
              .assignShowLoaderJobs(visualizationContainer.isDrawShowLoaders())
              .assignShowProvisionTargets(visualizationContainer.isDrawShowProvisioners())
              .assignShowAllMemberCounts(visualizationContainer.isDrawShowAllMemberCounts())
              .assignShowDirectMemberCounts(visualizationContainer.isDrawShowDirectMemberCounts())
              .assignMaxSiblings(visualizationContainer.getDrawMaxSiblings())
              .assignShowObjectTypes(visualizationContainer.isDrawShowObjectTypes())
              .assignIncludeGroupsInMemberCounts(visualizationContainer.isDrawIncludeGroupsInMemberCounts())
              .assignSubjectForIsMemberCheck(isMemberSubject);

      // filters on stems and groups (e.g., skip etc and the root folder by default).
      // future enhancement to make this configurable?
      String stemFilterRegexp = GrouperConfig.retrieveConfig().propertyValueString("visualization.skipFolderNamePatterns");
      String groupFilterRegexp = GrouperConfig.retrieveConfig().propertyValueString("visualization.skipGroupNamePatterns");
      if (!GrouperUtil.isBlank(stemFilterRegexp)) {
        relationGraph.assignSkipFolderNamePatterns(GrouperUtil.splitTrimToSet(stemFilterRegexp, ";"));
      }
      if (!GrouperUtil.isBlank(groupFilterRegexp)) {
        relationGraph.assignSkipGroupNamePatterns(GrouperUtil.splitTrimToSet(groupFilterRegexp, ";"));
      }

      final ProgressBean progressBean = new ProgressBean();
      progressBean.setStartedMillis(System.currentTimeMillis());
             
      visualizationContainer.setProgressBean(progressBean);

      // uniquely identifies this composite as opposed to other composite in other tabs
      String uniqueStemEditId = GrouperUuid.getUuid();
   
      visualizationContainer.setUniqueId(uniqueStemEditId);
       

      GrouperCallable grouperCallable = new GrouperCallable("visualization") {
         
        @Override
        public Void callLogic() {
          try {
 
            relationGraph.build();

            
            VisualizationGraph graph = null;
            String jsDrawFunctionName = null;

            if ("text".equals(visualizationContainer.getDrawModule())) {
              graph = buildToJsonText(relationGraph);
              jsDrawFunctionName = "drawGraphModuleText();";
            } else if ("d3".equals(visualizationContainer.getDrawModule())) {
              graph = buildToJsonD3(relationGraph, visualizationContainer);
              jsDrawFunctionName = "drawGraphModuleD3();";
            } else {
              throw new RuntimeException("Invalid visualization module: '" + visualizationContainer.getDrawModule() + "'");
            }
            
            visualizationContainer.setGraph(graph);
            visualizationContainer.setJsDrawFunctionName(jsDrawFunctionName);

            graph.addStatistic("numEdges", String.valueOf(relationGraph.getEdges().size()));
            graph.addStatistic("totalMemberCount", relationGraph.isShowAllMemberCounts() ? String.valueOf(relationGraph.getTotalMemberCount()) : "(not included)");
            graph.addStatistic("directMemberCount", relationGraph.isShowDirectMemberCounts() ? String.valueOf(relationGraph.getDirectMemberCount()) : "(not included)");
            graph.addStatistic("numNodes", String.valueOf(relationGraph.getNodes().size()));
            graph.addStatistic("numLoaderJobs", String.valueOf(relationGraph.getNumLoaders()));
            graph.addStatistic("numGroupsFromLoaders", String.valueOf(relationGraph.getNumGroupsFromLoaders()));
            graph.addStatistic("numProvisioners", String.valueOf(relationGraph.getNumProvisioners()));
            graph.addStatistic("numGroupsToProvisioners", String.valueOf(relationGraph.getNumGroupsToProvisioners()));
            graph.addStatistic("numSkippedFolders", String.valueOf(relationGraph.getNumSkippedFolders()));
            graph.addStatistic("numSkippedGroups", String.valueOf(relationGraph.getNumSkippedGroups()));

            graph.addSetting("startNode", relationGraph.getStartNode().getGrouperObject().getId());
            graph.addSetting("showAllMemberCounts", relationGraph.isShowAllMemberCounts());
            graph.addSetting("showDirectMemberCounts", relationGraph.isShowDirectMemberCounts());
            graph.addSetting("showObjectTypes", relationGraph.isShowObjectTypes());

 
            GrouperUtil.sleep(1000L * GrouperUiConfig.retrieveConfig().propertyValueInt("grouperUi.visualization.pauseInActionSeconds", 0));
             
          } catch (RuntimeException re) {
            progressBean.setHasException(true);
            progressBean.setException(re);
            // log this since the thread will just end and will never get logged
            LOG.error("error", re);
          } finally {
            // we done
            progressBean.setComplete(true);
          }
          return null;
        }
      };    
        
      // see if running in thread
      boolean useThreads = GrouperUiConfig.retrieveConfig().propertyValueBooleanRequired("grouperUi.composite.useThread");
    
      if (useThreads) {
          
        GrouperFuture grouperFuture = GrouperUtil.executorServiceSubmit(GrouperUtil.retrieveExecutorService(), grouperCallable);
          
        Integer waitForCompleteForSeconds = GrouperUiConfig.retrieveConfig().propertyValueInt("grouperUi.composite.progressStartsInSeconds");
    
        GrouperFuture.waitForJob(grouperFuture, waitForCompleteForSeconds);
             
      } else {
        grouperCallable.callLogic();
      }
 
      String sessionId = request.getSession().getId();
       
      MultiKey reportMultiKey = new MultiKey(sessionId, uniqueStemEditId);
       
      threadProgress.put(reportMultiKey, visualizationContainer);

      buildGraphStatusHelper(sessionId, uniqueStemEditId, true);
      

    } finally {
      GrouperSession.stopQuietly(grouperSession);
    }
  }

  /**
   * get the status of a progress screen
   */
  private void buildGraphStatusHelper(String sessionId, String uniqueId, boolean firstCall) {
     
    final Subject loggedInSubject = GrouperUiFilter.retrieveSubjectLoggedIn();
    
    Map debugMap = new LinkedHashMap();
     
    debugMap.put("method", "buildGraphStatusHelper");
    debugMap.put("sessionId", GrouperUtil.abbreviate(sessionId, 8));
    debugMap.put("uniqueId", GrouperUtil.abbreviate(uniqueId, 8));
   
    long startNanos = System.nanoTime();
    try {
      GuiResponseJs guiResponseJs = GuiResponseJs.retrieveGuiResponseJs();
   
      MultiKey multiKey = new MultiKey(sessionId, uniqueId);
       
      VisualizationContainer visualizationContainer = threadProgress.get(multiKey);
   
      if (visualizationContainer != null) {
         
        GrouperRequestContainer.retrieveFromRequestOrCreate().setVisualizationContainer(visualizationContainer);

        ProgressBean progressBean = visualizationContainer.getProgressBean();
     
        debugMap.put("elapsedSeconds", progressBean.getElapsedSeconds());
        
        // endless loop?
        if (progressBean.isThisLastStatus()) {
          return;
        }
         
        if (progressBean.isHasException()) {
          guiResponseJs.addAction(GuiScreenAction.newMessage(GuiMessageType.error,
              TextContainer.retrieveFromRequest().getText().get("visualizationException")));
          // it has an exception, leave it be
          threadProgress.put(multiKey, null);
          
          if (progressBean.getException() != null) {

            if (GrouperUiUtils.vetoHandle(guiResponseJs, progressBean.getException())) {
              return;
            }

            //dont change screens
            guiResponseJs.addAction(GuiScreenAction.newMessage(GuiMessageType.error, 
                GrouperUtil.xmlEscape(progressBean.getException().getMessage(), true)));

          }
          return;
        }
        // kick it off again?
        debugMap.put("complete", progressBean.isComplete());
        String jsDrawFunctionName = visualizationContainer.getJsDrawFunctionName();

        if (!progressBean.isComplete()) {
          
          guiResponseJs.addAction(GuiScreenAction.newInnerHtmlFromJsp("#progressDiv",
              "/WEB-INF/grouperUi2/visualization/visualizationWrapper.jsp"));

          if (firstCall) {
            
            guiResponseJs.addAction(GuiScreenAction.newScript("$('#vis-settings-form').hide('slow')"));

          }
          
          int progressRefreshSeconds = GrouperUiConfig.retrieveConfig().propertyValueInt("grouperUi.visualization.progressRefreshSeconds");
          progressRefreshSeconds = Math.max(progressRefreshSeconds, 1);
          progressRefreshSeconds *= 1000;
          guiResponseJs.addAction(GuiScreenAction.newScript("setTimeout(function() {ajax('../app/UiV2Visualization.buildGraphStatus?uniqueId=" + uniqueId + "')}, " + progressRefreshSeconds + ")"));
        } else {
          
          if (!firstCall) {
            
            guiResponseJs.addAction(GuiScreenAction.newInnerHtmlFromJsp("#progressDiv",
                "/WEB-INF/grouperUi2/visualization/visualizationWrapper.jsp"));

            jsDrawFunctionName = "if ($('#id_" + visualizationContainer.getUniqueId() + "').length > 0) {" + jsDrawFunctionName + "}";
          }

          VisualizationGraph graph = visualizationContainer.getGraph();

          guiResponseJs.addAction(GuiScreenAction.newScript("$('#vis-settings-form').show('slow')"));

          guiResponseJs.addAction(GuiScreenAction.newAssign("visualizationObject", graph));
          guiResponseJs.addAction(GuiScreenAction.newScript(jsDrawFunctionName));

          // it is complete, leave it be
          threadProgress.put(multiKey, null);
           
        }
      } else {
        //go back to main screen
        guiResponseJs.addAction(GuiScreenAction.newScript("guiV2link('operation=UiV2Main.indexMain');"));
      }
    } catch (RuntimeException re) {
      debugMap.put("exception", GrouperUtil.getFullStackTrace(re));
      throw re;
    } finally {
      if (LOG.isDebugEnabled()) {
        debugMap.put("tookMillis", (System.nanoTime()-startNanos)/1000000);
        LOG.debug(GrouperUtil.mapToString(debugMap));
      }

    }
   
   
  }

  
  /**
   * get the status of a stem edit
   * @param request
   * @param response
   */
  public void buildGraphStatus(HttpServletRequest request, HttpServletResponse response) {
    String sessionId = request.getSession().getId();
    String uniqueCompositeId = request.getParameter("uniqueId");
    final Subject loggedInSubject = GrouperUiFilter.retrieveSubjectLoggedIn();
    GrouperSession grouperSession = GrouperSession.start(loggedInSubject);

    try {
      buildGraphStatusHelper(sessionId, uniqueCompositeId, false);
    } finally {
      GrouperSession.stopQuietly(grouperSession);
    }
  }
  /**
   * keep an expirable cache of progress for 1 hours (longest an import is expected).  This has multikey of session id and some random uuid
   * uniquely identifies this action as opposed to other actions in other tabs
   */
  private static ExpirableCache threadProgress = new ExpirableCache(60);
  

  private String getGrouperObjectDisplayExtension(GraphNode graphNode) {
    String displayExtension = graphNode.getGrouperObjectName();
    if (graphNode.isGroup()) {
      displayExtension = ((Group)graphNode.getGrouperObject()).getDisplayExtension();
    } else if (graphNode.isStem()) {
      displayExtension = ((Stem)graphNode.getGrouperObject()).getDisplayExtension();
    }

    return displayExtension;
  }

  private D3Graph buildToJsonD3(RelationGraph relationGraph, VisualizationContainer visualizationContainer) {
    VisualSettings settings = new VisualSettings();
    VisualStyleSet styleSet = settings.getStyleSet("dot");

    D3Graph graph = new D3Graph();

    Set styleTypes = new HashSet();

    // create a lookup of all the edges that are left and right factors
    Map compositeLeftFactors = new HashMap();
    Map compositeRightFactors = new HashMap();
    loadLeftRightFactorMaps(relationGraph, compositeLeftFactors, compositeRightFactors);

    for (GraphNode graphNode: relationGraph.getNodes()) {
      Node node = new Node(
        graphNode.getGrouperObjectId(),
        graphNode.getGrouperObjectName(),
        getGrouperObjectDisplayExtension(graphNode),
        graphNode.getGrouperObject().getDescription(),
        graphNode.getStyleObjectType().getName(),
        styleSet.getStyleProperty(graphNode.getStyleObjectType().getName(), "baseType", graphNode.getStyleObjectType().getName()),
        styleSet.getStyleProperty(graphNode.getStyleObjectType().getName(), "linkType", ""),
        graphNode.getAllMemberCount(),
        graphNode.getDirectMemberCount(),
        graphNode.getObjectTypeNames()
      );

      addCustomTypeTags(node, graphNode);

      if (compositeLeftFactors.containsKey(graphNode)) {
        node.setCompositeLeftFactorId(compositeLeftFactors.get(graphNode));
      }
      if (compositeRightFactors.containsKey(graphNode)) {
        node.setCompositeRightFactorId(compositeRightFactors.get(graphNode));
      }

      graph.addNode(graphNode.getGrouperObjectId(), node);
      styleTypes.add(graphNode.getStyleObjectType());
    }

    for (GraphEdge graphEdge: relationGraph.getEdges()) {
      D3Link link = new D3Link(
        graphEdge.getFromNode().getGrouperObject().getId(),
        graphEdge.getToNode().getGrouperObject().getId(),
        graphEdge.getStyleObjectType().getName()
      );

      graph.addLink(link);
      styleTypes.add(graphEdge.getStyleObjectType());
    }

    // In addition to edges and nodes, the graph itself has a style
    styleTypes.add(StyleObjectType.GRAPH);

    for (StyleObjectType styleType: styleTypes) {
      for (String propertyName: new String[]{"shape", "style", "nodestyle", "color", "fontcolor", "border", "arrowtail",
              "dir", "linkType", "displayTag", "headlabel", "labeldistance", "fillcolor"}) {
        String propertyValue = styleSet.getStyleProperty(styleType.getName(), propertyName, "");
        if (!"".equals(propertyValue)) {
          graph.addStyleProperty(styleType.getName(), propertyName, propertyValue);
        }
      }
    }

    // For a minimal set of objects, define styles even if not being used, so they can be used in the legend.
    // Keep them in a separate "fallbackStyles" property, so that the regular "styles" property can be used
    // to detect which ones are actually being used
    for (String styleName: new String[]{"group", "edge_complement_left", "edge_complement_right", "edge_intersect_left",
            "edge_intersect_right", "simple_loader_group", "loader_group", "edge_loader", "start_group", "intersect_group", "complement_group",
            "group_is_not_member", "group_is_member"}) {
      for (String propertyName: new String[]{"shape", "style", "nodestyle", "color", "fontcolor", "border", "arrowtail",
              "dir", "headlabel", "labeldistance", "fillcolor"}) {
        String propertyValue = styleSet.getStyleProperty(styleName, propertyName, "");
        if (!"".equals(propertyValue)) {
          graph.addFallbackStyleProperty(styleName, propertyName, propertyValue);
        }
      }
    }

    graph.addSetting("showLegend", visualizationContainer.isDrawShowLegend());
    graph.addSetting("showObjectTypes", visualizationContainer.isDrawShowLegend());

    if (visualizationContainer.isDrawShowLegend() && visualizationContainer.isDrawShowObjectTypes() && relationGraph.getObjectTypesUsed().size() > 0) {
      
      StringBuilder objectTypesLegend = new StringBuilder();
      
      objectTypesLegend.append(TextContainer.retrieveFromRequest().getText().get("visualization.form.legend.objectTypeLegend"));
      
      // get which types were used in the right order
      for (String objectTypeName : GrouperObjectTypesSettings.getObjectTypeNames()) {
        if (relationGraph.getObjectTypesUsed().contains(objectTypeName)) {
          objectTypesLegend.append(TextContainer.retrieveFromRequest().getText().get("visualization.form.legend.objectTypeLegend." + objectTypeName));
        }
      }
      
      // add space to end
      objectTypesLegend.append("\\l");
      
      graph.addSetting("objectTypesLegend", objectTypesLegend.toString());
    }
    return graph;
  }

  private  TextGraph buildToJsonText(RelationGraph relationGraph) {
    VisualSettings settings = new VisualSettings();
    VisualStyleSet styleSet = settings.getStyleSet("text");

    TextGraph graph = new TextGraph();

    //maintain the set of object types in use
    Set styleTypes = new HashSet();

    List sortedNodes = new ArrayList(relationGraph.getNodes());
    Collections.sort(sortedNodes, new SortByIndent());

    // create a lookup of all the edges that are left and right factors
    Map compositeLeftFactors = new HashMap();
    Map compositeRightFactors = new HashMap();
    loadLeftRightFactorMaps(relationGraph, compositeLeftFactors, compositeRightFactors);

    for (GraphNode graphNode: sortedNodes) {
      long indent = graphNode.getDistanceFromStartNode() + relationGraph.getMaxParentDistance();
      if (indent < 0) {
        indent = 0;
      }

      TextNode node = new TextNode(
        graphNode.getGrouperObjectId(),
        graphNode.getGrouperObjectName(),
        getGrouperObjectDisplayExtension(graphNode),
        graphNode.getGrouperObject().getDescription(),
        graphNode.getStyleObjectType().getName(),
        styleSet.getStyleProperty(graphNode.getStyleObjectType().getName(), "baseType", graphNode.getStyleObjectType().getName()) /* getNodeBaseType(graphNode) */,
        styleSet.getStyleProperty(graphNode.getStyleObjectType().getName(), "linkType", "") /*getNodeLinkType(graphNode)*/,
        graphNode.getAllMemberCount(),
        graphNode.getDirectMemberCount(),
        graphNode.getObjectTypeNames(),
        indent);

      addCustomTypeTags(node, graphNode);

      styleTypes.add(graphNode.getStyleObjectType());

      for (GraphNode n: graphNode.getParentNodes()) {
        node.addParentNodeId(n.getGrouperObjectId());
      }

      for (GraphNode n: graphNode.getChildNodes()) {
        node.addChildNodeId(n.getGrouperObjectId());
      }

      if (compositeLeftFactors.containsKey(graphNode)) {
        node.setCompositeLeftFactorId(compositeLeftFactors.get(graphNode));
      }
      if (compositeRightFactors.containsKey(graphNode)) {
        node.setCompositeRightFactorId(compositeRightFactors.get(graphNode));
      }

      graph.addNode(graphNode.getGrouperObjectId(), node);
      graph.addSortedNodeId(graphNode.getGrouperObjectId());
      styleTypes.add(graphNode.getStyleObjectType());
    }

    for (StyleObjectType styleType: styleTypes) {
      for (String propertyName : new String[]{"linkType", "displayTag"}) {
        String propertyValue = styleSet.getStyleProperty(styleType.getName(), propertyName, "");
        if (!"".equals(propertyValue)) {
          graph.addStyleProperty(styleType.getName(), propertyName, propertyValue);
        }
      }
    }


    return graph;
  }

  // In addition to the normal object types (ref, basis, etc.), add loader, intersection, complement based on node
  private void addCustomTypeTags(Node node, GraphNode graphNode) {
    if (graphNode.isLoaderGroup()) {
      node.addObjectTypeName("loader");
    }
    if (graphNode.isComplementGroup()) {
      node.addObjectTypeName("complement");
    }
    if (graphNode.isIntersectGroup()) {
      node.addObjectTypeName("intersection");
    }
  }

  // loads lookup maps from a node to left/right factors, based on the edges that are composite types
  private void loadLeftRightFactorMaps(RelationGraph relationGraph, Map compositeLeftFactors, Map compositeRightFactors) {
    for (GraphEdge graphEdge : relationGraph.getEdges()) {
      if (graphEdge.getStyleObjectType() == StyleObjectType.EDGE_COMPLEMENT_LEFT
              || graphEdge.getStyleObjectType() == StyleObjectType.EDGE_INTERSECT_LEFT) {
        compositeLeftFactors.put(graphEdge.getFromNode(), graphEdge.getToNode().getGrouperObjectId());
      } else if (graphEdge.getStyleObjectType() == StyleObjectType.EDGE_COMPLEMENT_RIGHT
              || graphEdge.getStyleObjectType() == StyleObjectType.EDGE_INTERSECT_RIGHT) {
        compositeRightFactors.put(graphEdge.getFromNode(), graphEdge.getToNode().getGrouperObjectId());
      }
    }
  }


  /**
   * Custom sort for the text builder, sorts by nodes indentation level
   */
  private class SortByIndent implements Comparator {
    public int compare(GraphNode a, GraphNode b)
    {
      return (int) (a.getDistanceFromStartNode() - b.getDistanceFromStartNode());
    }
  }


  private void visualizationHelper(Subject loggedInSubject, HttpServletRequest request, VisualizationContainer visualizationContainer,
                                   boolean fromSubmission) {
    //todo filters etc

    if (request.getParameter("groupId")!=null) {
      visualizationContainer.setObjectId(request.getParameter("groupId"));
      visualizationContainer.setObjectType("group");
    } else if (request.getParameter("stemId")!=null) {
      visualizationContainer.setObjectId(request.getParameter("stemId"));
      visualizationContainer.setObjectType("stem");
    } else if (request.getParameter("subjectId")!=null) {
      visualizationContainer.setObjectId(request.getParameter("subjectId"));
      visualizationContainer.setObjectType("subject");
    } else if (request.getParameter("objectId")!=null) {
      visualizationContainer.setObjectId(request.getParameter("objectId"));
      visualizationContainer.setObjectType(request.getParameter("objectType"));
    }

    if ("group".equals(visualizationContainer.getObjectType())) {
      visualizationContainer.setOperation("UiV2Visualization.groupView");
    } else if ("stem".equals(visualizationContainer.getObjectType())) {
      visualizationContainer.setOperation("UiV2Visualization.stemView");
    } else if ("subject".equals(visualizationContainer.getObjectType())) {
      visualizationContainer.setOperation("UiV2Visualization.subjectView");
    }

    // todo this is mixing keyname and text errors
    if (StringUtils.isBlank(visualizationContainer.getObjectId())
      || StringUtils.isBlank(visualizationContainer.getObjectType())) {
      throw new RuntimeException("error.subject-summary.missing-parameter");
    }

    /* For params below, persist differences between the form and preferences */
    boolean prefsHaveChanged = false;

    UiV2VisualizationPreference prefsFromAttribute = GrouperUserDataApi.visualizationPrefs(
        GrouperUiUserData.grouperUiGroupNameForUserData(), loggedInSubject, UiV2VisualizationPreference.class);
    if (prefsFromAttribute == null) {
      prefsFromAttribute = new UiV2VisualizationPreference();
    }

    /* draw module */
    visualizationContainer.setDrawModule(request.getParameter("drawModule"), prefsFromAttribute.getDrawModule());
    if (!visualizationContainer.getDrawModule().equals(prefsFromAttribute.getDrawModule())) {
      prefsFromAttribute.setDrawModule(visualizationContainer.getDrawModule());
      prefsHaveChanged = true;
    }

    /* parent levels */
    if (request.getParameter("drawNumParentsLevels") == null && request.getParameter("drawNumParentsAll") == null) {
      // new form, not from submission, so use the prefs values
      visualizationContainer.setDrawNumParentsLevels(prefsFromAttribute.getDrawNumParentsLevels());
    } else {
      long drawNumParentLevels = GrouperUtil.longValue(request.getParameter("drawNumParentsLevels"), -1);
      if (GrouperUtil.booleanValue(request.getParameter("drawNumParentsAll"), false)) {
        drawNumParentLevels = -1;
      }
      visualizationContainer.setDrawNumParentsLevels(drawNumParentLevels);
      if (drawNumParentLevels != prefsFromAttribute.getDrawNumParentsLevels()) {
        prefsFromAttribute.setDrawNumParentsLevels(drawNumParentLevels);
        prefsHaveChanged = true;
      }
    }


    /* child levels */
    if (request.getParameter("drawNumChildrenLevels") == null && request.getParameter("drawNumChildrenAll") == null) {
      // new form, not from submission, so use the prefs values
      visualizationContainer.setDrawNumChildrenLevels(prefsFromAttribute.getDrawNumChildrenLevels());
    } else {
      long drawNumChildLevels = GrouperUtil.longValue(request.getParameter("drawNumChildrenLevels"), -1);
      if (GrouperUtil.booleanValue(request.getParameter("drawNumChildrenAll"), false)) {
        drawNumChildLevels = -1;
      }
      visualizationContainer.setDrawNumChildrenLevels(drawNumChildLevels);
      if (drawNumChildLevels != prefsFromAttribute.getDrawNumChildrenLevels()) {
        prefsFromAttribute.setDrawNumChildrenLevels(drawNumChildLevels);
        prefsHaveChanged = true;
      }
    }

    /* max siblings */
    if (request.getParameter("drawMaxSiblings") == null && request.getParameter("drawMaxSiblingsAll") == null) {
      // new form, not from submission, so use the prefs values
      visualizationContainer.setDrawMaxSiblings(prefsFromAttribute.getDrawMaxSiblings());
    } else {
      long drawMaxSiblings = GrouperUtil.longValue(request.getParameter("drawMaxSiblings"), -1);
      if (GrouperUtil.booleanValue(request.getParameter("drawMaxSiblingsAll"), false)) {
        drawMaxSiblings = -1;
      }
      visualizationContainer.setDrawMaxSiblings(drawMaxSiblings);
      if (drawMaxSiblings != prefsFromAttribute.getDrawMaxSiblings()) {
        prefsFromAttribute.setDrawMaxSiblings(drawMaxSiblings);
        prefsHaveChanged = true;
      }
    }

    /* name type */
    visualizationContainer.setDrawObjectNameType(request.getParameter("drawObjectNameType"), prefsFromAttribute.getDrawObjectNameType());
    if (!GrouperUtil.equals(visualizationContainer.getDrawObjectNameType(), prefsFromAttribute.getDrawObjectNameType())) {
      prefsFromAttribute.setDrawObjectNameType(visualizationContainer.getDrawObjectNameType());
      prefsHaveChanged = true;
    }

    /* For checkboxes, hard to tell whether it's a new form or a box set to unchecked.
     * Use the fromSubmission flag to tell the difference
     */

    /* show stems */
    if (fromSubmission) {
      visualizationContainer.setDrawShowStems(
        GrouperUtil.booleanValue(request.getParameter("drawShowStems"), false));
      if (visualizationContainer.isDrawShowStems() != prefsFromAttribute.isDrawShowStems()) {
        prefsFromAttribute.setDrawShowStems(visualizationContainer.isDrawShowStems());
        prefsHaveChanged = true;
      }
    } else {
      visualizationContainer.setDrawShowStems(prefsFromAttribute.isDrawShowStems());
    }

    /* show loaders */
    if (fromSubmission) {
      visualizationContainer.setDrawShowLoaders(
        GrouperUtil.booleanValue(request.getParameter("drawShowLoaders"), false));
      if (visualizationContainer.isDrawShowLoaders() != prefsFromAttribute.isDrawShowLoaders()) {
        prefsFromAttribute.setDrawShowLoaders(visualizationContainer.isDrawShowLoaders());
        prefsHaveChanged = true;
      }
    } else {
      visualizationContainer.setDrawShowLoaders(prefsFromAttribute.isDrawShowLoaders());
    }

  /* show provisioners */
  if (fromSubmission) {
    visualizationContainer.setDrawShowProvisioners(
      GrouperUtil.booleanValue(request.getParameter("drawShowProvisioners"), false));
    if (visualizationContainer.isDrawShowProvisioners() != prefsFromAttribute.isDrawShowProvisioners()) {
      prefsFromAttribute.setDrawShowProvisioners(visualizationContainer.isDrawShowProvisioners());
      prefsHaveChanged = true;
    }
  } else {
    visualizationContainer.setDrawShowProvisioners(prefsFromAttribute.isDrawShowProvisioners());
  }

    /* show all member counts */
    if (fromSubmission) {
      visualizationContainer.setDrawShowAllMemberCounts(
        GrouperUtil.booleanValue(request.getParameter("drawShowAllMemberCounts"), false));
      if (visualizationContainer.isDrawShowAllMemberCounts() != prefsFromAttribute.isDrawShowAllMemberCounts()) {
        prefsFromAttribute.setDrawShowAllMemberCounts(visualizationContainer.isDrawShowAllMemberCounts());
        prefsHaveChanged = true;
      }
    } else {
      visualizationContainer.setDrawShowAllMemberCounts(prefsFromAttribute.isDrawShowAllMemberCounts());
    }

    /* show direct member counts */
    if (fromSubmission) {
      visualizationContainer.setDrawShowDirectMemberCounts(
        GrouperUtil.booleanValue(request.getParameter("drawShowDirectMemberCounts"), false));
      if (visualizationContainer.isDrawShowDirectMemberCounts() != prefsFromAttribute.isDrawShowDirectMemberCounts()) {
        prefsFromAttribute.setDrawShowDirectMemberCounts(visualizationContainer.isDrawShowDirectMemberCounts());
        prefsHaveChanged = true;
      }
    } else {
      visualizationContainer.setDrawShowDirectMemberCounts(prefsFromAttribute.isDrawShowDirectMemberCounts());
    }

    /* show grouper object types */
    if (fromSubmission) {
      visualizationContainer.setDrawShowObjectTypes(
        GrouperUtil.booleanValue(request.getParameter("drawShowObjectTypes"), false));
      if (visualizationContainer.isDrawShowObjectTypes() != prefsFromAttribute.isDrawShowObjectTypes()) {
        prefsFromAttribute.setDrawShowObjectTypes(visualizationContainer.isDrawShowObjectTypes());
        prefsHaveChanged = true;
      }
    } else {
      visualizationContainer.setDrawShowObjectTypes(prefsFromAttribute.isDrawShowObjectTypes());
    }


    /* include groups in member counts */
    if (fromSubmission) {
      visualizationContainer.setDrawIncludeGroupsInMemberCounts(
        GrouperUtil.booleanValue(request.getParameter("drawIncludeGroupsInMemberCounts"), false));
      if (visualizationContainer.isDrawIncludeGroupsInMemberCounts() != prefsFromAttribute.isDrawIncludeGroupsInMemberCounts()) {
        prefsFromAttribute.setDrawIncludeGroupsInMemberCounts(visualizationContainer.isDrawIncludeGroupsInMemberCounts());
        prefsHaveChanged = true;
      }
    } else {
      visualizationContainer.setDrawIncludeGroupsInMemberCounts(prefsFromAttribute.isDrawIncludeGroupsInMemberCounts());
    }

    /* show legend */
    if (fromSubmission) {
      visualizationContainer.setDrawShowLegend(
        GrouperUtil.booleanValue(request.getParameter("drawShowLegend"), false));
      if (visualizationContainer.isDrawShowLegend() != prefsFromAttribute.isDrawShowLegend()) {
        prefsFromAttribute.setDrawShowLegend(visualizationContainer.isDrawShowLegend());
        prefsHaveChanged = true;
      }
    } else {
      visualizationContainer.setDrawShowLegend(prefsFromAttribute.isDrawShowLegend());
    }

    // force a find of the lazy-loaded based on the id and type
    //visualizationContainer.getGrouperObject();

    if (prefsHaveChanged) {
      GrouperUserDataApi.visualizationPrefsAssign(GrouperUiUserData.grouperUiGroupNameForUserData(), loggedInSubject, prefsFromAttribute);
    }
  }

  /**
   * search for a subject for visualization
   * @param request
   * @param response
   */
  public void addMemberSearch(HttpServletRequest request, HttpServletResponse response) {

    GrouperRequestContainer grouperRequestContainer = GrouperRequestContainer.retrieveFromRequestOrCreate();
    
    final Subject loggedInSubject = GrouperUiFilter.retrieveSubjectLoggedIn();
    GuiResponseJs guiResponseJs = GuiResponseJs.retrieveGuiResponseJs();
  
    GrouperSession grouperSession = null;
    
    try {
      grouperSession = GrouperSession.start(loggedInSubject);
  
  
      GroupContainer groupContainer = grouperRequestContainer.getGroupContainer();
  
      String searchString = request.getParameter("addMemberSubjectSearch");
      
      boolean searchOk = GrouperUiUtils.searchStringValid(searchString);
      if (!searchOk) {
        
        guiResponseJs.addAction(GuiScreenAction.newInnerHtml("#addMemberResults", 
            TextContainer.retrieveFromRequest().getText().get("groupAddMemberNotEnoughChars")));
        return;
      }

      String matchExactIdString = request.getParameter("matchExactId[]");
      boolean matchExactId = GrouperUtil.booleanValue(matchExactIdString, false);

      String sourceId = request.getParameter("sourceId");
      
      Set subjects = null;
      if (matchExactId) {
        if (GrouperConfig.retrieveConfig().propertyValueBoolean("grouperQuerySubjectsMultipleQueriesCommaSeparated", true)) {
          Set searchStrings = GrouperUtil.splitTrimToSet(searchString, ",");
          if (StringUtils.equals("all", sourceId)) {
            subjects = new LinkedHashSet(GrouperUtil.nonNull(SubjectFinder.findByIdsOrIdentifiers(searchStrings)).values());
          } else {
            subjects = new LinkedHashSet(GrouperUtil.nonNull(SubjectFinder.findByIdsOrIdentifiers(searchStrings, sourceId)).values());
          }
        } else {
          Subject subject = null;
          if (StringUtils.equals("all", sourceId)) {
            try {
              subject = SubjectFinder.findByIdOrIdentifier(searchString, false);
            } catch (SubjectNotUniqueException snue) {
              //ignore
            }
          } else {
            subject = SubjectFinder.findByIdOrIdentifierAndSource(searchString, sourceId, false);
          }

          subjects = new LinkedHashSet();
          if (subject != null) {
            subjects.add(subject);
          }
        }
      } else {
        if (StringUtils.equals("all", sourceId)) {
          subjects = SubjectFinder.findPage(searchString).getResults();
        } else {
          Set sources = GrouperUtil.toSet(SourceManager.getInstance().getSource(sourceId));
          subjects = SubjectFinder.findPage(searchString, sources).getResults();
        }
      }
      
      if (GrouperUtil.length(subjects) == 0) {

        guiResponseJs.addAction(GuiScreenAction.newInnerHtml("#addMemberResults", 
            TextContainer.retrieveFromRequest().getText().get("groupAddMemberNoSubjectsFound")));
        return;
      }
      
      Set guiSubjects = GuiSubject.convertFromSubjects(subjects, "uiV2.subjectSearchResults", 30);
      
      groupContainer.setGuiSubjectsAddMember(guiSubjects);
  
      guiResponseJs.addAction(GuiScreenAction.newInnerHtmlFromJsp("#addVisualizationMemberResults", 
          "/WEB-INF/grouperUi2/visualization/addVisualizationMemberResults.jsp"));
      
    } finally {
      GrouperSession.stopQuietly(grouperSession);
    }
  }

}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy