001package org.kuali.common.util.metainf.model;
002
003import com.google.common.collect.ImmutableList;
004import com.google.common.collect.Ordering;
005import org.apache.commons.lang3.EnumUtils;
006import org.apache.commons.lang3.StringUtils;
007import org.kuali.common.util.metainf.spring.MetaInfDataLocation;
008import org.kuali.common.util.metainf.spring.MetaInfDataType;
009import org.springframework.util.CollectionUtils;
010
011import java.util.Collections;
012import java.util.List;
013
014/**
015 * <p>
016 * Sort first by the data passed into the configurable comparator, then sort lexicographically by directory structure,
017 * then filename
018 * </p>
019 *
020 * For example:
021 *
022 * <pre>
023 *   2 - server/demo/a/foo2.txt         1 - client/bootstrap/a/foo1.txt
024 *   3 - server/demo/a/b/foo.txt        2 - server/demo/a/foo2.txt
025 *   1 - client/bootstrap/a/foo1.txt    3 - server/demo/a/b/foo.txt
026 * </pre>
027 *
028 */
029public class ConfigurablePathComparator extends PathComparator {
030
031    private List<String> qualifierOrder = Collections.emptyList();
032    private List<MetaInfDataLocation> locationOrder = Collections.emptyList();
033    private List<MetaInfDataType> typeOrder = Collections.emptyList();
034
035    @Override
036    protected int compare(int index, String[] tokens1, String[] tokens2) {
037        String string1 = tokens1[index];
038        String string2 = tokens2[index];
039
040        if (!CollectionUtils.isEmpty(qualifierOrder)) {
041            if (qualifierOrder.contains(string1) && qualifierOrder.contains(string2)) {
042                int compare = Ordering.explicit(qualifierOrder).compare(string1, string2);
043
044                // If the comparison comes back as anything but zero, we are done
045                if (compare != 0) {
046                    return compare;
047                }
048            }
049        }
050
051        if (!CollectionUtils.isEmpty(locationOrder)) {
052            if (isLocation(string1) && isLocation(string2)) {
053                MetaInfDataLocation location1 = MetaInfDataLocation.valueOf(StringUtils.upperCase(string1));
054                MetaInfDataLocation location2 = MetaInfDataLocation.valueOf(StringUtils.upperCase(string2));
055
056                int compare = Ordering.explicit(locationOrder).compare(location1, location2);
057
058                // If the comparison comes back as anything but zero, we are done
059                if (compare != 0) {
060                    return compare;
061                }
062            }
063        }
064
065        if (!CollectionUtils.isEmpty(typeOrder)) {
066            if (isType(string1) && isType(string2)) {
067                MetaInfDataType type1 = MetaInfDataType.valueOf(StringUtils.upperCase(string1));
068                MetaInfDataType type2 = MetaInfDataType.valueOf(StringUtils.upperCase(string2));
069
070                int compare = Ordering.explicit(typeOrder).compare(type1, type2);
071
072                // If the comparison comes back as anything but zero, we are done
073                if (compare != 0) {
074                    return compare;
075                }
076            }
077        }
078
079        return super.compare(index, tokens1, tokens2);
080    }
081
082    protected boolean isLocation(String token) {
083        return EnumUtils.isValidEnum(MetaInfDataLocation.class, StringUtils.upperCase(token));
084    }
085
086    protected boolean isType(String token) {
087        return EnumUtils.isValidEnum(MetaInfDataType.class, StringUtils.upperCase(token));
088    }
089
090    public static Builder builder() {
091        return new Builder();
092    }
093
094    public static class Builder {
095
096        // Optional
097        private List<String> qualifierOrder = Collections.emptyList();
098        private List<MetaInfDataLocation> locationOrder = Collections.emptyList();
099        private List<MetaInfDataType> typeOrder = Collections.emptyList();
100
101        public Builder qualifierOrder(List<String> qualifierOrder) {
102            this.qualifierOrder = qualifierOrder;
103            return this;
104        }
105
106        public Builder locationOrder(List<MetaInfDataLocation> locationOrder) {
107            this.locationOrder = locationOrder;
108            return this;
109        }
110
111        public Builder typeOrder(List<MetaInfDataType> typeOrder) {
112            this.typeOrder = typeOrder;
113            return this;
114        }
115
116        public ConfigurablePathComparator build() {
117            this.qualifierOrder = ImmutableList.copyOf(qualifierOrder);
118            this.locationOrder = ImmutableList.copyOf(locationOrder);
119            this.typeOrder = ImmutableList.copyOf(typeOrder);
120            return new ConfigurablePathComparator(this);
121        }
122
123    }
124
125    private ConfigurablePathComparator(Builder builder) {
126        this.qualifierOrder = builder.qualifierOrder;
127        this.locationOrder = builder.locationOrder;
128        this.typeOrder = builder.typeOrder;
129    }
130
131}