Package org.twodividedbyzero.idea.findbugs.gui.tree.model

Source Code of org.twodividedbyzero.idea.findbugs.gui.tree.model.GroupTreeModel

/*
* Copyright 2008-2013 Andre Pfeiler
*
* This file is part of FindBugs-IDEA.
*
* FindBugs-IDEA is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FindBugs-IDEA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with FindBugs-IDEA.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.twodividedbyzero.idea.findbugs.gui.tree.model;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import edu.umd.cs.findbugs.BugInstance;
import org.twodividedbyzero.idea.findbugs.common.DoneCallback;
import org.twodividedbyzero.idea.findbugs.common.EventDispatchThreadHelper;
import org.twodividedbyzero.idea.findbugs.common.ExtendedProblemDescriptor;
import org.twodividedbyzero.idea.findbugs.common.util.BugInstanceUtil;
import org.twodividedbyzero.idea.findbugs.gui.tree.BugInstanceComparator;
import org.twodividedbyzero.idea.findbugs.gui.tree.GroupBy;
import org.twodividedbyzero.idea.findbugs.gui.tree.model.Grouper.GrouperCallback;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;


/**
* $Date: 2013-12-05 08:09:56 -0600 (Thu, 05 Dec 2013) $
*
* @author Andre Pfeiler<andrep@twodividedbyzero.org>
* @version $Revision: 257 $
* @since 0.0.1
*/
public class GroupTreeModel extends AbstractTreeModel<VisitableTreeNode> implements GrouperCallback<BugInstance> {

  private static final long serialVersionUID = 0L;
  private static final Logger LOGGER = Logger.getInstance(GroupTreeModel.class.getName());

  private GroupBy[] _groupBy;
  private final transient Map<String, Map<Integer, List<BugInstanceGroupNode>>> _groups;
  private transient Grouper<BugInstance> _grouper;
  private final AtomicInteger _bugCount;
  private final transient Map<PsiFile, List<ExtendedProblemDescriptor>> _problems;
  private final transient Project _project;


  public GroupTreeModel(final VisitableTreeNode root, final GroupBy[] groupBy, final Project project) {
    _root = root;
    _project = project;
    _bugCount = new AtomicInteger(0);
    _groupBy = groupBy.clone();
    _groups = new ConcurrentHashMap<String, Map<Integer, List<BugInstanceGroupNode>>>();
    _problems = new ConcurrentHashMap<PsiFile, List<ExtendedProblemDescriptor>>();
  }


  Project getProject() {
    return _project;
  }


  private void addGroupIfAbsent(final String groupNameKey, final int depth, final BugInstanceGroupNode groupNode) {
    if (!_groups.containsKey(groupNameKey)) {
      final Map<Integer, List<BugInstanceGroupNode>> map = new ConcurrentHashMap<Integer, List<BugInstanceGroupNode>>();
      _groups.put(groupNameKey, map);
      final List<BugInstanceGroupNode> groupNodes = _addGroupIfAbsent(groupNameKey, depth, groupNode);
      map.put(depth, groupNodes);
    } else {
      _addGroupIfAbsent(groupNameKey, depth, groupNode);
    }
  }


  private List<BugInstanceGroupNode> _addGroupIfAbsent(final String groupNameKey, final int depth, final BugInstanceGroupNode groupNode) {
    final Map<Integer, List<BugInstanceGroupNode>> map = _groups.get(groupNameKey);

    if (map.containsKey(depth)) {
      final List<BugInstanceGroupNode> list = map.get(depth);
      list.add(groupNode);

      return list;
    } else {
      final List<BugInstanceGroupNode> list = new ArrayList<BugInstanceGroupNode>();
      list.add(groupNode);
      map.put(depth, list);

      return list;
    }
  }


  @SuppressWarnings({"ReturnOfCollectionOrArrayField"})
  public Map<PsiFile, List<ExtendedProblemDescriptor>> getProblems() {
    return _problems;
  }


  @SuppressWarnings({"MethodMayBeStatic", "AnonymousInnerClass"})
  private void addProblem(final BugInstanceNode leaf) {
    leaf.getPsiFile(new DoneCallback<PsiFile>() {
      public void onDone(final PsiFile value) {
        _addProblem(value, leaf);
      }
    });
  }


  private void _addProblem(final PsiFile value, final BugInstanceNode leaf) {
    if (value != null) {
      final ExtendedProblemDescriptor element = new ExtendedProblemDescriptor(value, leaf);
      if (_problems.containsKey(value)) {
        _problems.get(value).add(element);
      } else {
        final List<ExtendedProblemDescriptor> list = new ArrayList<ExtendedProblemDescriptor>();
        list.add(element);
        _problems.put(value, list);
      }
    }
  }


  public int getBugCount() {
    return _bugCount.get();
  }


  @SuppressWarnings({"LockAcquiredButNotSafelyReleased"})
  public void addNode(final BugInstance bugInstance) {
    /*if(isHiddenBugGroup(bugInstance)) {
      return;
    }*/
    /*EventDispatchThreadHelper.assertInEDT();
    EventDispatchThreadHelper.assertInADT();*/
    EventDispatchThreadHelper.assertInEDTorADT();

    //_lock.lock();
    /*if (!ApplicationManager.getApplication().isDispatchThread() || !EventQueue.isDispatchThread()) {
      _lock.lock();
    }*/
    _bugCount.getAndIncrement();
    group(bugInstance);
  }


  private void group(final BugInstance bugInstance) {
    if (_grouper == null) {
      _grouper = new Grouper<BugInstance>(this);
    }
    final List<Comparator<BugInstance>> groupComparators = BugInstanceComparator.getGroupComparators(_groupBy);
    _grouper.group(bugInstance, groupComparators);
  }


  public void startGroup(final BugInstance member, final int depth) {
    EventDispatchThreadHelper.assertInEDTorADT();

    final GroupBy groupBy = _groupBy[depth];
    final String groupName = GroupBy.getGroupName(groupBy, member);
    final BugInstanceGroupNode groupNode = new BugInstanceGroupNode(groupBy, groupName, _root, member, depth, _project);

    addGroupIfAbsent(Arrays.toString(BugInstanceUtil.getGroupPath(member, depth, _groupBy)), depth, groupNode);

    ((AbstractTreeNode<VisitableTreeNode>) _root).addChild(groupNode);
    nodeStructureChanged(_root);

    startSubGroup(depth + 1, member, member);
  }


  public void startSubGroup(final int depth, final BugInstance member, final BugInstance parent) {
    EventDispatchThreadHelper.assertInEDTorADT();

    String groupName = GroupBy.getGroupName(_groupBy[depth - 1], member);
    final BugInstanceGroupNode parentGroup = ((RootNode) _root).findChildNode(parent, depth - 1, groupName);

    //final BugInstanceGroupNode parentGroup = ((RootNode) _root).getChildByBugInstance(parent, depth - 1);
    //final BugInstanceGroupNode parentGroup = ((RootNode) _root).getChildByGroupName(GroupBy.getGroupName(_groupBy[depth - 1], parent), depth - 1);
    //final BugInstanceGroupNode parentGroup = _groups.get(depth-1 + Arrays.toString(getGroupPath(member)));

    if (parentGroup != null) {
      final GroupBy groupBy = _groupBy[depth];
      groupName = GroupBy.getGroupName(groupBy, member);
      final BugInstanceGroupNode childGroup = new BugInstanceGroupNode(groupBy, groupName, parentGroup, member, depth, _project);

      addGroupIfAbsent(Arrays.toString(BugInstanceUtil.getGroupPath(parent, depth, _groupBy)), depth, childGroup);
      //addGroupIfAbsent(GroupBy.getGroupName(_groupBy[0], parent), depth, childGroup);

      parentGroup.addChild(childGroup);
      nodeStructureChanged(parentGroup);

      if (depth < _groupBy.length - 1) {
        startSubGroup(depth + 1, member, member);
      } else {
        addToGroup(depth, member, member);
      }
    } else {
      //noinspection ThrowableInstanceNeverThrown
      LOGGER.error(new NullPointerException("parentGroup can not be null."));
    }


  }


  public void addToGroup(final int depth, final BugInstance member, final BugInstance parent) {
    EventDispatchThreadHelper.assertInEDTorADT();

    final String groupName = GroupBy.getGroupName(_groupBy[depth], member);
    final BugInstanceGroupNode parentGroup = ((RootNode) _root).findChildNode(parent, depth, groupName);

    if (parentGroup != null) {
      final BugInstanceNode childNode = new BugInstanceNode(member, parentGroup, _project);
      parentGroup.addChild(childNode);
      addProblem(childNode);
      nodeStructureChanged(parentGroup);
    } else {
      //noinspection ThrowableInstanceNeverThrown
      LOGGER.error("parentSubGroup can not be null. ", new NullPointerException());
    }
  }


  public Comparator<BugInstance> currentGroupComparatorChain(final int depth) {
    return BugInstanceComparator.getComparatorChain(depth, getGroupBy());
  }


  public List<BugInstance> availableGroups(final int depth, final BugInstance bugInstance) {
    final List<BugInstance> result = new ArrayList<BugInstance>();

    //final GroupBy groupBy = _groupBy[0];
    //final String groupName = GroupBy.getGroupName(groupBy, bugInstance);
    final String groupName = Arrays.toString(BugInstanceUtil.getGroupPath(bugInstance, depth, _groupBy));

    if (_groups.containsKey(groupName)) {
      final Map<Integer, List<BugInstanceGroupNode>> map = _groups.get(groupName);

      if (map.containsKey(depth)) {
        final List<BugInstanceGroupNode> groupNodes = map.get(depth);

        for (final BugInstanceGroupNode node : groupNodes) {
          result.add(node.getBugInstance());
        }
      }
    }

    return result;
  }


  public void setGroupBy(final GroupBy[] groupBy) {
    _groupBy = groupBy.clone();
  }


  public GroupBy[] getGroupBy() {
    return _groupBy.clone();
  }


  public void clear() {
    EventDispatchThreadHelper.assertInEDTorADT();

    //_sortedCollection.clear();
    _bugCount.set(0);
    _groups.clear();
    _problems.clear();
    ((AbstractTreeNode<VisitableTreeNode>) _root).removeAllChilds();
    nodeStructureChanged(_root);
    reload();

  }


  @Nullable
  public BugInstanceNode findNodeByBugInstance(final BugInstance bugInstance) {
    final String[] fullGroupPath = BugInstanceUtil.getFullGroupPath(bugInstance, _groupBy);
    final String[] groupNameKey = BugInstanceUtil.getGroupPath(bugInstance, fullGroupPath.length - 1, _groupBy);

    final Map<Integer, List<BugInstanceGroupNode>> map = _groups.get(Arrays.toString(groupNameKey));
    for (final Entry<Integer, List<BugInstanceGroupNode>> entry : map.entrySet()) {

      final List<BugInstanceGroupNode> groupNodes = entry.getValue();
      for (final BugInstanceGroupNode groupNode : groupNodes) {

        final List<VisitableTreeNode> bugInstanceNodes = groupNode.getChildsList();
        for (final VisitableTreeNode node : bugInstanceNodes) {
          final BugInstance bug = ((BugInstanceNode) node).getBugInstance();
          if (bug.equals(bugInstance)) {
            return (BugInstanceNode) node;
          }
        }
      }
    }


    return null;
  }


  /**
   * Returns the child of <I>parent</I> at index <I>index</I> in the
   * parent's child array. <I>parent</I> must be a node previously obtained
   * from this data source. This should not return null if <i>index</i> is a
   * valid index for <i>parent</i> (that is <i>index</i> >= 0 && <i>index</i> <
   * getChildCount(<i>parent</i>)).
   *
   * @param parent a node in the tree, obtained from this data source
   * @param index
   * @return the child of <I>parent</I> at index <I>index</I>
   */
  @Override
  public VisitableTreeNode getChildNode(final VisitableTreeNode parent, final int index) {
    return (VisitableTreeNode) parent.getChildAt(index);
  }


  /**
   * Returns the number of children of <I>parent</I>. Returns 0 if the node
   * is a leaf or if it has no children. <I>parent</I> must be a node
   * previously obtained from this data source.
   *
   * @param parent a node in the tree, obtained from this data source
   * @return the number of children of the node <I>parent</I>
   */
  @Override
  public int getChildNodeCount(final VisitableTreeNode parent) {
    return parent.getChildCount();
  }


  /**
   * Returns the index of child in parent. If either the parent or child is
   * <code>null</code>, returns -1.
   *
   * @param parent a note in the tree, obtained from this data source
   * @param child  the node we are interested in
   * @return the index of the child in the parent, or -1 if either the parent
   *         or the child is <code>null</code>
   */
  @Override
  public int getIndexOfChildNode(final VisitableTreeNode parent, final VisitableTreeNode child) {
    return parent.getIndex(child);
  }


  /**
   * Obtain the parent of a node.
   *
   * @param child a node
   * @return the parent of the child (or null if child has no parent).
   */
  @Override
  public VisitableTreeNode getParentNode(final VisitableTreeNode child) {
    return (VisitableTreeNode) child.getParent();
  }


  /**
   * Will be called prior to removal of the current root node. Sub classes
   * must remove listeners that were added previously by <code>install</code>.
   *
   * @param root the root node that is about to be be removed.
   * @see #install(Object)
   */
  @Override
  protected void deinstall(final VisitableTreeNode root) {
    EventDispatchThreadHelper.assertInEDT();
    EventDispatchThreadHelper.assertInADT();
  }


  /**
   * Subclasses must implement this method and return a <code>Class</code>
   * object for the generic type.
   *
   * @return the <code>Class</code> for the generic type
   */
  @Override
  protected Class<VisitableTreeNode> getNodeClass() {
    return VisitableTreeNode.class;
  }


  /**
   * Will be called immediately after setting of the root node. Sub classes
   * may add listeners to the root that enable them to monitor changes to the
   * tree and fire change events accordingly.
   *
   * @param root the root node that is just installed.
   * @see #deinstall(Object)
   */
  @Override
  protected void install(final VisitableTreeNode root) {
    EventDispatchThreadHelper.assertInEDT();
    EventDispatchThreadHelper.assertInADT();
  }
}
TOP

Related Classes of org.twodividedbyzero.idea.findbugs.gui.tree.model.GroupTreeModel

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.