/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2012 Servoy BV
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option) any
later version.
This program 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along
with this program; if not, see http://www.gnu.org/licenses or write to the Free
Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
*/
package com.servoy.extension.dependency;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import com.servoy.extension.VersionStringUtils;
/**
* A path chooser able to use multiple tree path comparators to finally reach it's chosen path.
* For example if it is set to use 3 comparators c1, c2 and c3, it sorts based on c1, then in case of c1 equals sorts on c2 then on c3.
* @author acostescu
*/
public class MultipleCriteriaChooser implements IDependencyPathChooser
{
protected Comparator<DependencyPath>[] comparators;
public MultipleCriteriaChooser()
{
this(null);
}
public MultipleCriteriaChooser(Comparator<DependencyPath> comparators[])
{
this.comparators = comparators;
}
@SuppressWarnings("unchecked")
public DependencyPath pickResolvePath(List<DependencyPath> allResolvedPaths)
{
final Comparator<DependencyPath> comps[];
if (comparators == null)
{
comps = new Comparator[] { new LibConflictPathComparator(), new ReplacementPathComparator(), new ShortestPathComparator(), new HigherVersionComparator() };
}
else
{
comps = comparators;
}
Collections.sort(allResolvedPaths, new Comparator<DependencyPath>()
{
public int compare(DependencyPath o1, DependencyPath o2)
{
int result = 0;
int i = 0;
while (result == 0 && i < comps.length)
{
result = comps[i].compare(o1, o2);
i++;
}
return result;
}
});
return allResolvedPaths.get(0);
}
/**
* Paths with no valid lib choices are first, then paths with choices, then paths with conflicts.
*/
public static class LibConflictPathComparator implements Comparator<DependencyPath>
{
public int compare(DependencyPath o1, DependencyPath o2)
{
int result;
boolean o1HasChoices = false;
boolean o1HasConflicts = false;
boolean o2HasChoices = false;
boolean o2HasConflicts = false;
if (o1.libChoices != null)
{
o1HasChoices = true;
for (LibChoice lc : o1.libChoices)
{
if (lc.conflict) o1HasConflicts = true;
}
}
if (o2.libChoices != null)
{
o2HasChoices = true;
for (LibChoice lc : o2.libChoices)
{
if (lc.conflict) o2HasConflicts = true;
}
}
if (o1HasChoices)
{
if (o2HasChoices)
{
if (o1HasConflicts)
{
if (o2HasConflicts) result = 0;
else result = 1;
}
else
{
if (o2HasConflicts) result = -1;
else result = 0;
}
}
else
{
result = 1;
}
}
else
{
if (o2HasChoices)
{
result = -1;
}
else
{
result = 0;
}
}
return result;
}
}
/**
* No upgrades/downgrades first, then only upgrades, then paths which contain downgrades but no upgrades, then both.<br>
* First extension node in the path is ignored.
*/
public static class ReplacementPathComparator implements Comparator<DependencyPath>
{
public int compare(DependencyPath o1, DependencyPath o2)
{
int result;
boolean o1Upgrade = false;
boolean o1Downgrade = false;
boolean o2Upgrade = false;
boolean o2Downgrade = false;
for (int i = o1.extensionPath.length - 1; i > 0; i--) // ignore first
{
if (o1.extensionPath[i].resolveType == ExtensionNode.UPGRADE_RESOLVE) o1Upgrade = true;
else if (o1.extensionPath[i].resolveType == ExtensionNode.DOWNGRADE_RESOLVE) o1Downgrade = true;
}
for (int i = o2.extensionPath.length - 1; i > 0; i--) // ignore first
{
if (o2.extensionPath[i].resolveType == ExtensionNode.UPGRADE_RESOLVE) o2Upgrade = true;
else if (o2.extensionPath[i].resolveType == ExtensionNode.DOWNGRADE_RESOLVE) o2Downgrade = true;
}
if (o1Upgrade)
{
if (o2Upgrade)
{
if (o1Downgrade)
{
if (o2Downgrade) result = 0; // UD == UD
else result = 1; // UD > U
}
else
{
if (o2Downgrade) result = -1; // U < UD
else result = 0; // U = U
}
}
else
{
if (o1Downgrade)
{
result = 1; // UD > D, UD > S
}
else
{
if (o2Downgrade) result = -1; // U < D
else result = 1; // U > S
}
}
}
else
{
if (o2Upgrade)
{
if (o1Downgrade)
{
if (o2Downgrade) result = -1; // D < UD
else result = 1; // D > U
}
else
{
result = -1; // S < U, S < UD
}
}
else
{
if (o1Downgrade)
{
if (o2Downgrade) result = 0; // D = D
else result = 1; // D > S
}
else
{
if (o2Downgrade) result = -1; // S < D
else result = 0; // S == S
}
}
}
return result;
}
}
/**
* While going through the two paths, as long as they have the same extensions but different versions, the higher version is first.
*/
public static class HigherVersionComparator implements Comparator<DependencyPath>
{
public int compare(DependencyPath o1, DependencyPath o2)
{
int i = 0;
int result;
do
{
if (o1.extensionPath[i].id.equals(o2.extensionPath[i].id))
{
result = VersionStringUtils.compareVersions(o1.extensionPath[i].version, o2.extensionPath[i].version);
}
else
{
result = o1.extensionPath[i].id.compareTo(o2.extensionPath[i].id);
}
i++;
}
while (result == 0 && i < o1.extensionPath.length && i < o2.extensionPath.length);
return result;
}
}
/**
* Shorter path is always first.
*/
public static class ShortestPathComparator implements Comparator<DependencyPath>
{
public int compare(DependencyPath o1, DependencyPath o2)
{
return o1.extensionPath.length - o2.extensionPath.length;
}
}
}