001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019
020package org.apache.isis.core.metamodel.layout.memberorderfacet;
021
022import java.util.Comparator;
023import java.util.StringTokenizer;
024
025import org.apache.isis.core.metamodel.facets.members.order.MemberOrderFacet;
026
027public class MemberOrderFacetComparator implements Comparator<MemberOrderFacet> {
028
029    private boolean ensureInSameGroup;
030    public MemberOrderFacetComparator(boolean ensureInSameGroup) {
031        this.ensureInSameGroup = ensureInSameGroup;
032    }
033    
034    @Override
035    public int compare(final MemberOrderFacet m1, final MemberOrderFacet m2) {
036        if (m1 == null && m2 == null) {
037            return 0;
038        }
039
040        if (m1 == null && m2 != null) {
041            return +1; // annotated before non-annotated
042        }
043        if (m1 != null && m2 == null) {
044            return -1; // annotated before non-annotated
045        }
046
047        if (ensureInSameGroup && !m1.name().equals(m2.name())) {
048            throw new IllegalArgumentException("Not in same group");
049        }
050
051        final String sequence1 = m1.sequence();
052        final String sequence2 = m2.sequence();
053
054        final String[] components1 = componentsFor(sequence1);
055        final String[] components2 = componentsFor(sequence2);
056
057        final int length1 = components1.length;
058        final int length2 = components2.length;
059
060        // shouldn't happen but just in case.
061        if (length1 == 0 && length2 == 0) {
062            return 0;
063        }
064
065        // continue to loop until we run out of components.
066        int n = 0;
067        while (true) {
068            final int length = n + 1;
069            // check if run out of components in either side
070            if (length1 < length && length2 >= length) {
071                return -1; // o1 before o2
072            }
073            if (length2 < length && length1 >= length) {
074                return +1; // o2 before o1
075            }
076            if (length1 < length && length2 < length) {
077                // run out of components
078                return 0;
079            }
080            // we have this component on each side
081
082            int componentCompare = 0;
083            try {
084                final Integer c1 = Integer.valueOf(components1[n]);
085                final Integer c2 = Integer.valueOf(components2[n]);
086                componentCompare = c1.compareTo(c2);
087            } catch (final NumberFormatException nfe) {
088                // not integers compare as strings
089                componentCompare = components1[n].compareTo(components2[n]);
090            }
091
092            if (componentCompare != 0) {
093                return componentCompare;
094            }
095            // this component is the same; lets look at the next
096            n++;
097        }
098    }
099
100    private static String[] componentsFor(final String sequence) {
101        final StringTokenizer tokens = new StringTokenizer(sequence, ".", false);
102        final String[] components = new String[tokens.countTokens()];
103        for (int i = 0; tokens.hasMoreTokens(); i++) {
104            components[i] = tokens.nextToken();
105        }
106        return components;
107    }
108
109
110}