/* * ==================================================================== * * The ObjectStyle Group Software License, Version 1.0 * * Copyright (c) 2002 - 2007 The ObjectStyle Group and individual authors of the * software. 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. The end-user documentation included with the redistribution, if any, * must include the following acknowlegement: "This product includes software * developed by the ObjectStyle Group (http://objectstyle.org/)." Alternately, * this acknowlegement may appear in the software itself, if and wherever such * third-party acknowlegements normally appear. * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse * or promote products derived from this software without prior written * permission. For written permission, please contact andrus@objectstyle.org. * 5. Products derived from this software may not be called "ObjectStyle" nor * may "ObjectStyle" appear in their names without prior written permission of * the ObjectStyle Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 THE * OBJECTSTYLE GROUP OR ITS CONTRIBUTORS 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. * ==================================================================== * * This software consists of voluntary contributions made by many individuals * on behalf of the ObjectStyle Group. For more information on the ObjectStyle * Group, please see <http://objectstyle.org/> . * */ package org.objectstyle.woenvironment.frameworks; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; public abstract class DependencyOrdering<T extends Dependency> { // We need to track with frameworks were projects, because they "win" when competing // with the same named framework from a /Frameworks folder private Set<String> projectFrameworkNames; // We need to track the name of the framework that contained each entry so we can // look it back up when we're building the final classpath private Map<T, String> dependencyFramework; private Map<String, List<T>> frameworkDependencies; // We also need to keep track of the location of the framework, so we only load // jars from the first framework we come across private Map<String, String> addedFrameworkPaths; private boolean _includeProjectDependency; public DependencyOrdering(boolean includeProjectDependency) { _includeProjectDependency = includeProjectDependency; } // Pending results contains all of the classpath entries, filtered such that we only // load the first of a framework from a /Frameworks folder, but we may end up with // dupes that are in a project AND a /Frameworks folder -- we'll clean that up later. protected List<T> pendingResult; protected void initialize() { projectFrameworkNames = new HashSet<String>(); dependencyFramework = new HashMap<T, String>(); frameworkDependencies = new HashMap<String, List<T>>(); addedFrameworkPaths = new LinkedHashMap<String, String>(); pendingResult = new LinkedList<T>(); } public List<T> orderDependencies(List<T> dependencies) { initialize(); for (T dependency : dependencies) { String dependencyRawPath = dependency.getRawPath(); String[] dependencyRawSegments = dependencyRawPath.split("[\\/]"); // rewrite the raw path from its segments to normalize it against // other paths we will make (just makes sure they're all consistent) dependencyRawPath = joinRawPath(dependencyRawSegments); String frameworkName = null; int frameworkSegment = frameworkSegmentForPath(dependencyRawSegments); boolean addDependency = false; if (frameworkSegment == -1) { // MS: If ".framework" isn't in the path and we have a project, then // put a "fake" entry in the framework list corresponding to the project. This // prevents /Library/Framework versions of the framework from loading later on // in the classpath. if (dependency.isProject()) { frameworkName = dependency.getProjectFrameworkName(); addedFrameworkPaths.put(frameworkName, dependencyRawPath); projectFrameworkNames.add(frameworkName); } addDependency = true; } else { // MS: Otherwise, we have a regular framework path. In this case, we // want to skip any jar that is coming from a different path for the // framework than we have previously loaded. frameworkName = dependencyRawSegments[frameworkSegment]; String frameworkPath = joinRawPath(dependencyRawSegments, frameworkSegment + 1); String previousFrameworkPath = addedFrameworkPaths.get(frameworkName); if (previousFrameworkPath == null) { addDependency = true; addedFrameworkPaths.put(frameworkName, frameworkPath); } else if (previousFrameworkPath.equals(frameworkPath)) { addDependency = true; } } // MS: ... all the stars have aligned, and this is a valid entry. Lets add it. if (addDependency) { if (frameworkName != null) { dependencyFramework.put(dependency, frameworkName); List<T> thisFrameworkDependencies = frameworkDependencies.get(frameworkName); if (thisFrameworkDependencies == null) { thisFrameworkDependencies = new LinkedList<T>(); frameworkDependencies.put(frameworkName, thisFrameworkDependencies); } thisFrameworkDependencies.add(dependency); } // MS: We need to get the build/BuiltFramework.framework folder from // a project and add that instead of the bin folder ... if (dependency.isWOProject()) { addWOProject(dependency); } else { pendingResult.add(dependency); } } } // sort classpath: project in front, then frameworks, then apple frameworks, then the rest List<T> processedDeps = new LinkedList<T>(); List<T> otherDeps = new LinkedList<T>(); List<T> nonAppleDeps = new LinkedList<T>(); List<T> appleDeps = new LinkedList<T>(); List<T> projectDeps = new LinkedList<T>(); List<T> woaDeps = new LinkedList<T>(); for (T dependency : pendingResult) { if (!processedDeps.contains(dependency)) { String frameworkName = dependencyFramework.get(dependency); if (dependency.isProject()) { // Don't double-add project deps -- Remove the /bin folder, because the build/App.woa/Contents/Resources/Java version will also be in there if (_includeProjectDependency || !dependency.isWOProject()) { projectDeps.add(dependency); } } // If the framework was added as a project, don't add it as a /Frameworks // folder framework. This is cleaning up from the case where we got, for // instance /Library/Frameworks/WOOgnl.framework AND WOOgnl project. We // want the project to win. else if (!projectFrameworkNames.contains(frameworkName)) { //System.out.println("DependencyOrdering.orderDependencies: " + dependency.getLocation()); if (dependency.isAppleProvided()) { //System.out.println("DependencyOrdering.orderDependencies: is apple"); addDependencies(dependency, frameworkName, appleDeps, processedDeps); } else if (dependency.isFrameworkJar()) { //System.out.println("DependencyOrdering.orderDependencies: is framework jar"); addDependencies(dependency, frameworkName, nonAppleDeps, processedDeps); } else if (dependency.isBuildProject()) { //System.out.println("DependencyOrdering.orderDependencies: is build project"); addDependencies(dependency, frameworkName, nonAppleDeps, processedDeps); } else if (dependency.isWoa()) { //System.out.println("DependencyOrdering.orderDependencies: is woa"); addDependencies(dependency, frameworkName, woaDeps, processedDeps); } else { //System.out.println("DependencyOrdering.orderDependencies: is other"); addDependencies(dependency, frameworkName, otherDeps, processedDeps); } } } // else { // System.out.println("WORuntimeClasspathProvider.resolveClasspath: skipping " + frameworkName + ": " + entry); // } } List<T> sortedDependencies = new ArrayList<T>(); if (woaDeps.size() > 0) { sortedDependencies.addAll(woaDeps); } if (projectDeps.size() > 0) { sortedDependencies.addAll(projectDeps); } if (nonAppleDeps.size() > 0) { sortedDependencies.addAll(nonAppleDeps); } if (appleDeps.size() > 0) { sortedDependencies.addAll(appleDeps); } if (otherDeps.size() > 0) { sortedDependencies.addAll(otherDeps); } // for (IRuntimeClasspathEntry entry : sortedEntries) { // System.out.println("WORuntimeClasspathProvider.resolveClasspath: final = " + entry); // } return sortedDependencies; } protected void addDependencies(T dependency, String frameworkName, List<T> categorizedDeps, List<T> processedDeps) { if (frameworkName == null) { categorizedDeps.add(dependency); processedDeps.add(dependency); } else { List<T> thisFrameworkDeps = frameworkDependencies.get(frameworkName); if (thisFrameworkDeps == null) { categorizedDeps.add(dependency); processedDeps.add(dependency); } else { categorizedDeps.addAll(thisFrameworkDeps); processedDeps.addAll(thisFrameworkDeps); } } } protected abstract void addWOProject(T dependency); protected String joinRawPath(String[] pathSegments) { return joinRawPath(pathSegments, pathSegments.length); } protected String joinRawPath(String[] pathSegments, int length) { StringBuffer path = new StringBuffer(); for (int i = 0; i < length; i++) { path.append(pathSegments[i]); if (i < length - 1) { path.append("/"); } } return path.toString(); } protected int frameworkSegmentForPath(String[] pathSegments) { int frameworkSegment = -1; for (int segmentNum = 0; frameworkSegment == -1 && segmentNum < pathSegments.length; segmentNum++) { String segment = pathSegments[segmentNum]; if (segment.endsWith(".framework")) { frameworkSegment = segmentNum; } } return frameworkSegment; } }