Package org.mj.eclipse.reporting.classpath.actions

Source Code of org.mj.eclipse.reporting.classpath.actions.OpenEditor

/**
* Copyright (c) 2008, Mounir Jarraï
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*    1. Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*    2. Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*    3. All advertising materials mentioning features or use of this software
*       must display the following acknowledgement:
*      This product includes software developed by Mounir Jarraï
*        and its contributors.
*    4. Neither the name Mounir Jarraï nor the names of its contributors may
*       be used to endorse or promote products derived from this software
*       without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY MOUNIR JARRAÏ ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL MOUNIR JARRAÏ BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package org.mj.eclipse.reporting.classpath.actions;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.apache.commons.collections15.ArrayStack;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ILock;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.mj.eclipse.reporting.classpath.Activator;
import org.mj.eclipse.reporting.classpath.Editor;
import org.mj.eclipse.reporting.classpath.OnMemoryEditorInput;
import org.mj.eclipse.reporting.classpath.actions.ModelFactory.IProjectDependenciesProvider.DependenciesDirection;
import org.mj.eclipse.reporting.classpath.mvc.models.IConnector;
import org.mj.eclipse.reporting.classpath.mvc.models.IDiagram;
import org.mj.eclipse.reporting.classpath.mvc.models.INode;
import org.mj.eclipse.reporting.classpath.mvc.models.internal.DiagramModel;
import org.mj.eclipse.reporting.classpath.preferences.PreferenceConstants;

/**
* @author Mounir Jarraï
*
*/
class ModelFactory {

  private static final ILog LOGGER = Activator.getDefault().getLog();

  static interface IProjectDependenciesProvider {

    public enum DependenciesDirection {
      TOP_DOWN, BOTTOM_UP
    }

    /**
     * @param project
     * @return the project's dependencies as an array of <code>IProject</code> instance.
     */
    IProject[] getDependencies(IProject project) throws Exception;

    /**
     * @return the dependecies navigation direction.
     */
    DependenciesDirection getDirection();

  }

  static IStatus computeModel(final IDiagram model, IProjectDependenciesProvider dependenciesProvider, IProgressMonitor monitor) {
    // Using stack to avoid recursive algorithm
    ArrayStack<IProject> stack = new ArrayStack<IProject>();
    // Used to remember traversed nodes
    Set<IProject> doneSet = new HashSet<IProject>();
    try {
      IProject project = model.getRootProject();

      // Simulate recursive function fist call.
      stack.push(project);
      monitor.beginTask("Create Model", IProgressMonitor.UNKNOWN);

      while (!stack.isEmpty()) {
        // Simulate recursive function exit.
        project = stack.pop();
        doneSet.add(project);
        if (stack.contains(project) && !doneSet.contains(project)) {
          System.out.println("found cycle on " + project + " : " + stack.toString() + "    DONE: " + doneSet.toString());
          continue;
        }

        IProject[] ReferencedProjects = dependenciesProvider.getDependencies(project);
        for (IProject referencedProject : ReferencedProjects) {
          monitor.subTask("Analyse " + project.getName() + " project dependencies");
          if (monitor.isCanceled()) {
            return Status.CANCEL_STATUS;
          }

          IConnector createdConnector = null;
          if (DependenciesDirection.TOP_DOWN.equals(dependenciesProvider.getDirection())) {
            createdConnector = model.createConnector(project, referencedProject);
          } else if (DependenciesDirection.BOTTOM_UP.equals(dependenciesProvider.getDirection())) {
            createdConnector = model.createConnector(referencedProject, project);
          }

          // Simulate recursive function call.
          if (stack.contains(referencedProject)) {
            // System.out.println(createdConnector + " : " + stack.toString() + "    DONE: " + doneSet.toString());
          }
          if (!doneSet.contains(referencedProject)) {
            // Found new node that is not already processed.
            stack.push(referencedProject);
          } else {
            // Don't traverse a node that is already traversed.
            // Do nothing
          }

          monitor.worked(1);
        }
      }

      LOGGER.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Dependencies model contains " + model.getProjects().size()
          + " projects and " + model.getConnectors().size() + " connections"));

      if (model.getProjects().isEmpty()) {
        model.createProject(model.getRootProject());
      }
     
      long maxDeg = 0;
      long minDeg = Long.MAX_VALUE;
      for (INode node : model.getProjects()) {
        minDeg = Math.min(minDeg, node.getIncomingConnections().size() + node.getOutgoingConnections().size());
        maxDeg = Math.max(maxDeg, node.getIncomingConnections().size() + node.getOutgoingConnections().size());
      }
      LOGGER.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Graph min degree = " + minDeg));
      LOGGER.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Graph max degree = " + maxDeg));
      return Status.OK_STATUS;
    } catch (Exception e) {
      return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Oops ! Stupid thing happends", e);
    } finally {
      doneSet.clear();
      stack.clear();
    }
  }

  /**
   * @param src,
   *            source node
   * @param dst,
   *            target node
   * @return a <code>List<List<IConnector>></code> instance that contains all possible path between source and target nodes or
   *         <code>null</code> if operation is canceled
   * @throws Throwable
   */
  @SuppressWarnings("unchecked")
  static List<List<IConnector>> backTrackingPath(final INode src, final INode dst, final IProgressMonitor monitor) throws Throwable {
    if (src == null || dst == null) {
      throw new IllegalArgumentException("path, src and dst parameters can't be null");
    }

    List<List<IConnector>> allPath = new ArrayList<List<IConnector>>();
    if (src.equals(dst)) {
      return allPath;
    }

    // Using stack to avoid recursive algorithm
    IStack<MemoPoint> stack = null;
    try {
      stack = new OnMemoryStack<MemoPoint>(); //VirtualMemoryStack<MemoPoint>();
      // Simulate recursive function fist call.
      stack.push(new MemoPoint(src, new ArrayList<IConnector>(), new ArrayStack<INode>()));
      while (!stack.isEmpty()) {
        // Simulate recursive function exit.
        MemoPoint memoPoint = stack.pop();
        Collection<IConnector> outgoingConnection = memoPoint.src.getOutgoingConnections();
        for (IConnector connector : outgoingConnection) {
          if (connector.isInCycle()) {
            continue;
          }
          // Makes a copy of the current path to enable back tracking if no solution is found.
          ArrayList<IConnector> subPath = (ArrayList<IConnector>) memoPoint.path.clone();
          subPath.add(connector);

          INode connectorSource = connector.getSource();
          INode connectorTarget = connector.getTarget();
          if (!connectorTarget.equals(src) && !connectorTarget.equals(memoPoint.src)) {
            // -- VERSION 2: Check for cycle --           
            int cycleStart = -1;
            int cycleEnd = -1;
            ArrayStack<INode> compressedSubPath = (ArrayStack<INode>) memoPoint.compressedPath.clone(); // using only nodes (No edge) {startNode, nextNode, nextNode, ..., endNode}
            if (compressedSubPath.isEmpty()) {
              // Add startNode
              compressedSubPath.push(connectorSource);
              compressedSubPath.push(connectorTarget);
            } else if (compressedSubPath.contains(connectorTarget)) {
              // CYCLE FOUND
              cycleStart = compressedSubPath.indexOf(connectorTarget);
              cycleEnd = compressedSubPath.size() - 1;
            } else {
              // Add nextNode
              compressedSubPath.push(connectorTarget);
            }
            if (cycleStart != -1 && cycleEnd != -1) {
              ArrayList<IConnector> cyclePath = new ArrayList<IConnector>(cycleEnd - cycleStart + 1);
              // mark cycle
              for (int i = cycleStart; i <= cycleEnd; i++) {
                IConnector inCycleConnector = subPath.get(i);
                inCycleConnector.setInCycle(true);
                cyclePath.add(inCycleConnector);
              }
              Status status = new Status(IStatus.WARNING, Activator.PLUGIN_ID, "Cycle found : " + cyclePath
                  + " as subset of path : " + subPath);
              LOGGER.log(status);
              continue;
            }
            // -- VERSION 2 :  End cycle detection as subset of path --

            if (connectorTarget.equals(dst)) {
              // Path from src to dst is found
              allPath.add(subPath);
              continue;
            } else {
              // Simulate recursive function call.
              MemoPoint mp = new MemoPoint(connectorTarget, subPath, compressedSubPath);
              stack.push(mp);
            }
          } else {
            // CYCLE FOUND = subPath
            Status status = new Status(IStatus.WARNING, Activator.PLUGIN_ID, "Cycle found in path : " + subPath);
            LOGGER.log(status);
            for (IConnector inCycleConnector : subPath) {
              inCycleConnector.setInCycle(true);
            }
            continue;
          }
        }
      }
      return allPath;
    } catch (Throwable t) {
      throw t;
    } finally {
      if (stack != null) {
        stack.clear();
      }
    }
  }

  /**
   * @param model
   * @param monitor
   * @return
   */
  static IStatus computePathCost(final IDiagram model, final IProgressMonitor monitor) {
    List<IConnector> connectors = model.getConnectors();

    // Initialize thread pool
    ExecutorService threadPool = Executors.newFixedThreadPool(Activator.getDefault().getPluginPreferences().getInt(
        PreferenceConstants.THREAD_POOL_SIZE));

    monitor.beginTask("Compute connections costs", connectors.size());
    Collection<Callable<IStatus>> callables = new ArrayList<Callable<IStatus>>(connectors.size());

    long startTime = System.currentTimeMillis();
    for (final IConnector connector : connectors) {
      //      if (monitor.isCanceled()) {
      //        return Status.CANCEL_STATUS;
      //      }
      Callable<IStatus> callable = new
      /**
       * @author bipbip
       *
       */
      Callable<IStatus>() {
        /**
         * @see java.util.concurrent.Callable#call()
         */
        public IStatus call() throws Exception {
          synchronized (monitor) {
            monitor.subTask("Compute path " + connector + " cost");
          }

          // Back Tracing path
          List<List<IConnector>> allPath;
          try {
            allPath = backTrackingPath(connector.getSource(), connector.getTarget(), monitor);
            if (allPath == null) {
              return Status.CANCEL_STATUS;
            }
          } catch (Throwable t) {
            throw new Exception(t);
          }
          synchronized (LOGGER) {
            if (Activator.getDefault().getPluginPreferences().getBoolean(PreferenceConstants.LOG_POSSIBLE_PATHS_OCCURENCE)) {
              Status status = new Status(IStatus.INFO, Activator.PLUGIN_ID, allPath.size() + " possible paths for "
                  + connector);
              LOGGER.log(status);
            }
          }
          // compute path cost
          int maxCost = connector.getCost();
          for (List<IConnector> path : allPath) {
            synchronized (monitor) {
              if (monitor.isCanceled()) {
                return Status.CANCEL_STATUS;
              }
            }
            int pathCost = 0;
            for (IConnector subConnector : path) {
              pathCost += subConnector.getCost();
            }
            maxCost = Math.max(maxCost, pathCost);
          }
          connector.setCost(maxCost);
          synchronized (monitor) {
            monitor.worked(1);
          }
          return Status.OK_STATUS;
        }
      };
      callables.add(callable);
    }

    List<Future<IStatus>> futures = null;
    try {
      futures = threadPool.invokeAll(callables);
      threadPool.shutdownNow();
      long endTime = System.currentTimeMillis();
      long timeInSec = (endTime - startTime) / 1000;
      long sec = timeInSec % 60;
      long min = (timeInSec - sec) / 60;
      LOGGER.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "computePathCost takes " + (endTime - startTime) + "ms (" + min + "m "
          + sec + "s) when using a pool of "
          + Activator.getDefault().getPluginPreferences().getInt(PreferenceConstants.THREAD_POOL_SIZE) + "thread"));
      // Check sub callables status.
      for (Future<IStatus> future : futures) {
        if (!future.get().isOK()) {
          return future.get();
        }
      }
    } catch (InterruptedException e) {
      return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Oops ! Stupid thing happends", e);
    } catch (ExecutionException e) {
      return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Oops ! Stupid thing happends", e);
    }
    return Status.OK_STATUS;
  }

  /**
   * @param project
   */
  static void createAndSimplifyModel(IProject project, final IProjectDependenciesProvider dependenciesProvider) {
    IDiagram model;
    // Create model.
    model = new DiagramModel(project);

    final IDiagram modelRef = model;

    // Creating the model
    IProgressMonitor pm = Job.getJobManager().createProgressGroup();
    pm.beginTask("Compute Model", IProgressMonitor.UNKNOWN);
    final ILock lock = Job.getJobManager().newLock();
    Job createModeJob = new Job("Create Model") {
      /**
       * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
       */
      @Override
      protected IStatus run(IProgressMonitor monitor) {
        try {
          lock.acquire();
          //                return ModelFactory.computeBottomUpDependenciesModel(modelRef, monitor);
          return ModelFactory.computeModel(modelRef, dependenciesProvider, monitor);
        } finally {
          lock.release();
        }
      }
    };
    createModeJob.setUser(true);
    createModeJob.setProgressGroup(pm, IProgressMonitor.UNKNOWN);
    createModeJob.setThread(new Thread());
    createModeJob.schedule();

    // Creates job : Computing model connection's costs
    final IDiagram workingModel = model;
    Job simplifyModelJob = new Job("Simplify Model") {
      /**
       * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
       */
      @Override
      protected IStatus run(IProgressMonitor monitor) {
        try {
          lock.acquire();
          // IStatus computePathCostSatus = Status.OK_STATUS;
          IStatus computePathCostSatus = ModelFactory.computePathCost(workingModel, monitor);
          if (!Status.OK_STATUS.equals(computePathCostSatus)) {
            return computePathCostSatus;
          }

          IStatus editorStatus = null;
          // Open Editor within UI thread.
          if (Display.getCurrent() == null) {
            // Not in UI Thread
            class OpenEditor implements Runnable {
              IStatus status;

              public void run() {
                status = openEditor(workingModel);
              }
            }

            Display display = PlatformUI.getWorkbench().getDisplay();
            OpenEditor openEditor = new OpenEditor();
            display.syncExec(openEditor);
            editorStatus = openEditor.status;
          } else {
            // In UI Thread
            editorStatus = openEditor(workingModel);
          }
          monitor.done();
          return editorStatus;
        } finally {
          lock.release();
        }
      }
    };
    simplifyModelJob.setUser(true);
    simplifyModelJob.setProgressGroup(pm, IProgressMonitor.UNKNOWN);
    simplifyModelJob.setThread(new Thread());
    simplifyModelJob.schedule();

    pm.done();
  }

  /**
   * @param workingModel
   */
  static IStatus openEditor(final IDiagram workingModel) {
    try {
      IWorkbench workbench = PlatformUI.getWorkbench();
      IWorkbenchWindow activeWorkbenchWindow = workbench.getActiveWorkbenchWindow();
      final IWorkbenchPage activePage = activeWorkbenchWindow.getActivePage();
      activePage.openEditor(new OnMemoryEditorInput(workingModel), Editor.ID, true);
    } catch (PartInitException e) {
      Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Oops ! Stupid thing happends when opening editor : "
          + e.getLocalizedMessage(), e);
      LOGGER.log(status);
      return status;
    }
    return Status.OK_STATUS;
  }

  /**
   * @author mjarraï
   *
   */
  private static final class MemoPoint implements Serializable {
    ArrayList<IConnector> path;
    INode src;
    ArrayStack<INode> compressedPath;// using only nodes (No edge) {startNode, nextNode, nextNode, ..., endNode}

    /**
     * @param src
     * @param path
     */
    MemoPoint(INode src, ArrayList<IConnector> path, ArrayStack<INode> compressedPath) {
      this.path = path;
      this.src = src;
      this.compressedPath = compressedPath;
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
      return this.src.toString() + " : " + this.path.toString();
    }

    /**
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + ((path == null)
          ? 0
          : path.hashCode());
      result = prime * result + ((src == null)
          ? 0
          : src.hashCode());
      return result;
    }

    /**
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      final MemoPoint other = (MemoPoint) obj;
      if (path == null) {
        if (other.path != null)
          return false;
      } else if (!path.equals(other.path))
        return false;
      if (src == null) {
        if (other.src != null)
          return false;
      } else if (!src.equals(other.src))
        return false;
      return true;
    }
  }

}
TOP

Related Classes of org.mj.eclipse.reporting.classpath.actions.OpenEditor

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.