/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library 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 Lesser General Public License for more
* details.
*/
package com.liferay.portal.osgi.web.servlet.context.helper.internal.order;
import com.liferay.portal.kernel.util.ArrayUtil;
import com.liferay.portal.kernel.util.ListUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.osgi.web.servlet.context.helper.definition.WebXMLDefinition;
import com.liferay.portal.osgi.web.servlet.context.helper.order.Order;
import com.liferay.portal.osgi.web.servlet.context.helper.order.Order.Path;
import com.liferay.portal.osgi.web.servlet.context.helper.order.OrderBeforeAndAfterException;
import com.liferay.portal.osgi.web.servlet.context.helper.order.OrderCircularDependencyException;
import com.liferay.portal.osgi.web.servlet.context.helper.order.OrderMaxAttemptsException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author Vernon Singleton
* @author Juan Gonzalez
*/
public class OrderUtil {
public static List<WebXMLDefinition> getOrderedWebXMLDefinitions(
List<WebXMLDefinition> webXMLDefinitions,
List<String> absoluteOrderingNames)
throws OrderBeforeAndAfterException, OrderCircularDependencyException,
OrderMaxAttemptsException {
if (ListUtil.isEmpty(absoluteOrderingNames)) {
return _getOrderedWebXMLDefinitions(webXMLDefinitions);
}
return _getOrderedWebXMLDefinitions(
webXMLDefinitions, absoluteOrderingNames);
}
private static String[] _appendAndSort(String[]... namesArray) {
Map<String, Integer> map = new HashMap<>();
if (namesArray[0] != null) {
if (Arrays.binarySearch(namesArray[0], Order.OTHERS) >= 0) {
map.put(Order.OTHERS, 1);
}
}
for (String[] names : namesArray) {
for (String name : names) {
if (!name.equals(Order.OTHERS)) {
map.put(name, 1);
}
}
}
Set<String> set = map.keySet();
String[] orderedNames = set.toArray(new String[set.size()]);
Arrays.sort(orderedNames);
return orderedNames;
}
private static void _checkForBothBeforeAndAfter(
WebXMLDefinition webXMLDefinition)
throws OrderBeforeAndAfterException {
Order order = webXMLDefinition.getOrder();
EnumMap<Order.Path, String[]> routes = order.getRoutes();
Map<String, Integer> map = new HashMap<>();
String[] beforeNames = routes.get(Order.Path.BEFORE);
for (String beforeName : beforeNames) {
Integer value = map.get(beforeName);
if (value == null) {
value = 1;
}
else {
value += 1;
}
map.put(beforeName, value);
}
String[] afterNames = routes.get(Order.Path.AFTER);
for (String afterName : afterNames) {
Integer value = map.get(afterName);
if (value == null) {
value = 1;
}
else {
value += 1;
}
map.put(afterName, value);
}
Set<String> set = map.keySet();
String[] names = set.toArray(new String[set.size()]);
for (String name : names) {
if (map.get(name) > 1) {
throw new OrderBeforeAndAfterException(
webXMLDefinition.getFragmentName(), name);
}
}
}
private static void _checkForSpecExceptions(
List<WebXMLDefinition> webXMLDefinitions)
throws OrderBeforeAndAfterException, OrderCircularDependencyException {
for (WebXMLDefinition webXMLDefinition : webXMLDefinitions) {
_checkForBothBeforeAndAfter(webXMLDefinition);
for (Order.Path path : Order.Path.values()) {
_mapRoutes(webXMLDefinition, path, webXMLDefinitions);
}
}
}
private static List<String> _getFragmentNames(
WebXMLDefinition[] webXMLDefinitions) {
List<String> fragmentNames = new LinkedList<>();
for (WebXMLDefinition webXMLDefinition : webXMLDefinitions) {
fragmentNames.add(webXMLDefinition.getFragmentName());
}
return fragmentNames;
}
private static List<WebXMLDefinition> _getOrderedWebXMLDefinitions(
List<WebXMLDefinition> webXMLDefinitions)
throws OrderBeforeAndAfterException, OrderCircularDependencyException,
OrderMaxAttemptsException {
_checkForSpecExceptions(webXMLDefinitions);
webXMLDefinitions = _preSort(webXMLDefinitions);
WebXMLDefinition[] webXMLDefinitionsArray = webXMLDefinitions.toArray(
new WebXMLDefinition[webXMLDefinitions.size()]);
_innerSort(webXMLDefinitionsArray);
_postSort(webXMLDefinitionsArray);
return new ArrayList<>(Arrays.asList(webXMLDefinitionsArray));
}
private static List<WebXMLDefinition> _getOrderedWebXMLDefinitions(
List<WebXMLDefinition> webXMLDefinitions,
List<String> absoluteOrderingNames) {
List<WebXMLDefinition> orderedWebXMLDefinitions = new ArrayList<>();
List<WebXMLDefinition> tempWebXMLDefinitions =
new CopyOnWriteArrayList<>();
tempWebXMLDefinitions.addAll(webXMLDefinitions);
for (String absoluteOrderingName : absoluteOrderingNames) {
if (Order.OTHERS.equals(absoluteOrderingName)) {
continue;
}
boolean found = false;
for (WebXMLDefinition webXMLDefinition : tempWebXMLDefinitions) {
String fragmentName = webXMLDefinition.getFragmentName();
if (!found && absoluteOrderingName.equals(fragmentName)) {
found = true;
orderedWebXMLDefinitions.add(webXMLDefinition);
tempWebXMLDefinitions.remove(webXMLDefinition);
}
else if (found && absoluteOrderingName.equals(fragmentName)) {
break;
}
}
}
int index = absoluteOrderingNames.indexOf(Order.OTHERS);
if (index != -1) {
for (WebXMLDefinition webXMLDefinition : tempWebXMLDefinitions) {
orderedWebXMLDefinitions.add(index, webXMLDefinition);
}
}
return orderedWebXMLDefinitions;
}
private static Map<String, WebXMLDefinition> _getWebXMLDefinitionsMap(
List<WebXMLDefinition> webXMLDefinitions) {
Map<String, WebXMLDefinition> webXMLDefinitionsMap = new HashMap<>();
for (WebXMLDefinition webXMLDefinition : webXMLDefinitions) {
String fragmentName = webXMLDefinition.getFragmentName();
webXMLDefinitionsMap.put(fragmentName, webXMLDefinition);
}
return webXMLDefinitionsMap;
}
private static int _innerSort(WebXMLDefinition[] webXMLDefinitions)
throws OrderMaxAttemptsException {
boolean attempting = true;
int attempts = 0;
while (attempting) {
if (attempts > _MAX_ATTEMPTS) {
throw new OrderMaxAttemptsException(_MAX_ATTEMPTS);
}
attempting = false;
int last = webXMLDefinitions.length - 1;
for (int i = 0; i < webXMLDefinitions.length; i++) {
int x = i;
int y = x + 1;
if (x == last) {
y = x;
x = 0;
}
if (_isDisordered(webXMLDefinitions[x], webXMLDefinitions[y])) {
WebXMLDefinition webXMLDefinition = webXMLDefinitions[x];
webXMLDefinitions[x] = webXMLDefinitions[y];
webXMLDefinitions[y] = webXMLDefinition;
attempting = true;
}
}
attempts++;
}
return attempts;
}
private static boolean _isDisordered(
WebXMLDefinition webXMLDefinition1,
WebXMLDefinition webXMLDefinition2) {
Order order1 = webXMLDefinition1.getOrder();
Order order2 = webXMLDefinition2.getOrder();
if (order1.isOrdered() && !order2.isOrdered()) {
EnumMap<Path, String[]> routes = order1.getRoutes();
if (!ArrayUtil.isEmpty(routes.get(Order.Path.AFTER)) &&
!order1.isBeforeOthers()) {
return true;
}
}
if (order2.isBefore(webXMLDefinition1.getFragmentName()) ||
order1.isAfter(webXMLDefinition2.getFragmentName())) {
return true;
}
if (order1.isAfterOthers() &&
!order1.isBefore(webXMLDefinition2.getFragmentName()) &&
!(order1.isAfterOthers() &&
order2.isAfterOthers())) {
return true;
}
if (order2.isBeforeOthers() &&
!order2.isAfter(webXMLDefinition1.getFragmentName()) &&
!(order1.isBeforeOthers() &&
order2.isBeforeOthers())) {
return true;
}
return false;
}
private static void _mapRoutes(
WebXMLDefinition webXMLDefinition, Order.Path path,
List<WebXMLDefinition> webXMLDefinitions)
throws OrderCircularDependencyException {
Order order = webXMLDefinition.getOrder();
EnumMap<Order.Path, String[]> orderRoutes = order.getRoutes();
String[] pathNames = orderRoutes.get(path);
for (String pathName : pathNames) {
if (pathName.equals(Order.OTHERS)) {
continue;
}
for (WebXMLDefinition curWebXMLDefinition : webXMLDefinitions) {
if (!pathName.equals(curWebXMLDefinition.getFragmentName())) {
continue;
}
Order curOrder = curWebXMLDefinition.getOrder();
EnumMap<Order.Path, String[]> curRoutes = curOrder.getRoutes();
String[] curPathNames = curRoutes.get(path);
String fragmentName = webXMLDefinition.getFragmentName();
if (Arrays.binarySearch(curPathNames, fragmentName) >= 0) {
throw new OrderCircularDependencyException(
path, webXMLDefinitions);
}
Order.Path oppositePath = null;
if (path == Order.Path.BEFORE) {
oppositePath = Order.Path.AFTER;
}
else {
oppositePath = Order.Path.BEFORE;
}
String[] oppositePathNames = curRoutes.get(oppositePath);
if (Arrays.binarySearch(oppositePathNames, fragmentName) < 0) {
EnumMap<Order.Path, String[]> routes = new EnumMap<>(
Order.Path.class);
routes.put(path, curPathNames);
routes.put(
oppositePath,
_appendAndSort(
curRoutes.get(oppositePath),
new String[] {fragmentName}));
curOrder.setRoutes(routes);
}
if (ArrayUtil.isNotEmpty(curPathNames)) {
EnumMap<Order.Path, String[]> routes = new EnumMap<>(
Order.Path.class);
routes.put(path, _appendAndSort(pathNames, curPathNames));
routes.put(oppositePath, orderRoutes.get(oppositePath));
order.setRoutes(routes);
}
}
}
}
private static void _postSort(WebXMLDefinition[] webXMLDefinitions) {
int i = 0;
while (i < webXMLDefinitions.length) {
List<String> fragmentNames = _getFragmentNames(webXMLDefinitions);
boolean done = true;
for (int j = 0; j < webXMLDefinitions.length; j++) {
int k = 0;
for (String curFragmentName : fragmentNames) {
if (curFragmentName.equals(
webXMLDefinitions[j].getFragmentName())) {
break;
}
Order order = webXMLDefinitions[j].getOrder();
if (order.isBefore(curFragmentName)) {
WebXMLDefinition webXMLDefinition = null;
for (int l = 0; l < webXMLDefinitions.length; l++) {
if (l == k) {
webXMLDefinition = webXMLDefinitions[l];
}
if ((webXMLDefinition != null) && (l != j)) {
webXMLDefinitions[l] = webXMLDefinitions[l + 1];
}
if (l == j) {
webXMLDefinitions[l] = webXMLDefinition;
done = false;
break;
}
}
if (!done) {
break;
}
}
k = k + 1;
}
}
if (done) {
break;
}
}
}
private static List<WebXMLDefinition> _preSort(
List<WebXMLDefinition> webXMLDefinitions) {
List<WebXMLDefinition> preSortWebXMLDefinitions = new ArrayList<>();
Map<String, Integer> map = new LinkedHashMap<>();
List<WebXMLDefinition> tempWebXMLDefinitions = new LinkedList<>();
for (WebXMLDefinition webXMLDefinition : webXMLDefinitions) {
Order order = webXMLDefinition.getOrder();
if (Validator.isNull(webXMLDefinition.getFragmentName()) &&
!order.isOrdered()) {
tempWebXMLDefinitions.add(webXMLDefinition);
}
else {
EnumMap<Order.Path, String[]> routes = order.getRoutes();
String[] afterPathNames = routes.get(Order.Path.AFTER);
String[] beforePathNames = routes.get(Order.Path.BEFORE);
map.put(
webXMLDefinition.getFragmentName(),
afterPathNames.length + beforePathNames.length);
}
}
map = _sortDescendingByValue(map);
Map<String, WebXMLDefinition> webXMLDefinitionsMap =
_getWebXMLDefinitionsMap(webXMLDefinitions);
for (Map.Entry<String, Integer> entry : map.entrySet()) {
String key = entry.getKey();
preSortWebXMLDefinitions.add(webXMLDefinitionsMap.get(key));
}
for (WebXMLDefinition webXMLDefinition : tempWebXMLDefinitions) {
preSortWebXMLDefinitions.add(webXMLDefinition);
}
return preSortWebXMLDefinitions;
}
private static Map<String, Integer> _sortDescendingByValue(
Map<String, Integer> map) {
List<Map.Entry<String, Integer>> list = new LinkedList<>(
map.entrySet());
Collections.sort(list, _COMPARATOR);
Map<String, Integer> sortedMap = new LinkedHashMap<>();
for (Map.Entry<String, Integer> entry : list) {
sortedMap.put(entry.getKey(), entry.getValue());
}
return sortedMap;
}
private static final Comparator<Map.Entry<String, Integer>> _COMPARATOR =
new MapEntryComparator();
private static final int _MAX_ATTEMPTS =
Integer.MAX_VALUE / (Byte.MAX_VALUE * Byte.MAX_VALUE * Byte.MAX_VALUE);
private static class MapEntryComparator
implements Comparator<Map.Entry<String, Integer>> {
@Override
public int compare(
Map.Entry<String, Integer> map1, Map.Entry<String, Integer> map2) {
Integer integer1 = map1.getValue();
Integer integer2 = map2.getValue();
return integer2.compareTo(integer1);
}
}
}