/* * Copyright (C) 2007 Snorre Gylterud, Stein Magnus Jodal, Johannes Knutsen, * Erik Bagge Ottesen, Ralf Bjarne Taraldset, and Iterate AS * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. */ package no.ntnu.mmfplanner.model; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * Abstract class for project sorters. */ public abstract class ProjectSorter implements Runnable { public static class Result { public int periods[]; public String sequence; public int npv; public double optimal; public int loss; @Override public String toString() { return sequence + ": " + npv + " " + Arrays.toString(periods); } } /** * Update the sequence string, but only if not already set * * @param r the result to update the sequence string for */ public void updateSequence(Result r) { if (r.sequence != null) { return; } StringBuffer sb = new StringBuffer(r.periods.length); for (int p = 1; p <= project.getPeriods(); p++) { for (int i = 0; i < r.periods.length; i++) { if (r.periods[i] == p) { sb.append(project.get(i).getId()); } } } r.sequence = sb.toString(); } /** * Update the optimal and loss values according to maxNpv * * @param maxNpv the maximal npv among all results */ private static void updateOptimalLoss(Result r, int maxNpv) { r.loss = maxNpv - r.npv; r.optimal = (double) r.npv / maxNpv; } public static class ResultComparator implements Comparator<Result> { public int compare(Result o1, Result o2) { return o2.npv - o1.npv; } } private static final int MAX_RESULTS = 40; private static final Comparator<? super Result> RESULT_COMPARATOR = new ResultComparator(); private List<Result> results; protected int minResultNpv; private long progress; private long progressMax; private boolean stopFlag; private boolean done; protected Project project; public ProjectSorter(Project project) { this.project = project; this.results = new ArrayList<Result>(); this.progress = 0; this.progressMax = 1; this.minResultNpv = -1; } /** * Returns how far the sorting progress has come. Should not return the same * value as getProgressMax() until isDone() returns true. * * @return the sort progress between 0 and getProgressMax() */ public synchronized long getProgress() { return this.progress; } /** * * @return maximal progress value */ public synchronized long getProgressMax() { return this.progressMax; } /** * List of ordered results, where the topmost is the most optimal. */ public synchronized List<Result> getResults() { if (results.size() > 0) { Collections.sort(results, RESULT_COMPARATOR); int maxNpv = results.get(0).npv; String lastSequence = null; int lastNpv = 0; for (int i = results.size() - 1; i >= 0; i--) { Result result = results.get(i); updateSequence(result); // remove duplicates (can occur when AB and BA is both placed in // the same period) if ((result.npv == lastNpv) && result.sequence.equals(lastSequence)) { results.remove(i); continue; } lastNpv = result.npv; lastSequence = result.sequence; if (isDone() && !isStopFlag()) { updateOptimalLoss(result, maxNpv); } } } return new ArrayList<Result>(results); } /** * @return true if done, false otherwise */ public synchronized boolean isDone() { return done; } /** * Adds a new Result with the given values * * @param npv the total npv of the sequence * @param periods the given periods for all mmfs in the project */ public synchronized void addResult(int npv, int periods[]) { if (npv <= minResultNpv) { return; } Result result = new Result(); result.npv = npv; result.periods = periods; results.add(result); // remove result with minimum npv if (results.size() > MAX_RESULTS) { int min = Integer.MAX_VALUE; Result remove = null; for (Result r : results) { if (r.npv < min) { min = r.npv; remove = r; } } minResultNpv = min; results.remove(remove); } } public synchronized void setProgress(long progress) { this.progress = progress; } public synchronized void setProgressMax(long progressMax) { this.progressMax = progressMax; } /** * Sets the stop flag, attempting to stop the sorting algorithm while * running. */ public synchronized void setStopFlag() { this.stopFlag = true; } public synchronized void setDone(boolean done) { this.done = done; } protected synchronized boolean isStopFlag() { return stopFlag; } public Project getProject() { return this.project; } protected abstract void sort(); /** * * @param threaded true if the sorter should run as a thread, false * otherwise. */ public void start(boolean threaded) { stopFlag = false; setDone(false); if (threaded) { new Thread(this).start(); } else { run(); } } public void run() { try { sort(); } catch (Exception e) { e.printStackTrace(); } setDone(true); } }