1/*
2 * Copyright (C) 2007-2010 Júlio Vilmar Gesser.
3 * Copyright (C) 2011, 2013-2016 The JavaParser Team.
4 *
5 * This file is part of JavaParser.
6 *
7 * JavaParser can be used either under the terms of
8 * a) the GNU Lesser General Public License as published by
9 *     the Free Software Foundation, either version 3 of the License, or
10 *     (at your option) any later version.
11 * b) the terms of the Apache License
12 *
13 * You should have received a copy of both licenses in LICENCE.LGPL and
14 * LICENCE.APACHE. Please refer to those files for details.
15 *
16 * JavaParser is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU Lesser General Public License for more details.
20 */
21
22package com.github.javaparser.utils;
23
24import com.github.javaparser.Position;
25import com.github.javaparser.Range;
26import com.github.javaparser.ast.Node;
27import com.github.javaparser.ast.NodeList;
28import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
29import com.github.javaparser.ast.body.FieldDeclaration;
30import com.github.javaparser.ast.body.MethodDeclaration;
31import com.github.javaparser.ast.expr.AnnotationExpr;
32import com.github.javaparser.ast.nodeTypes.NodeWithAnnotations;
33import com.github.javaparser.ast.nodeTypes.NodeWithType;
34import com.github.javaparser.ast.type.Type;
35
36import java.util.List;
37
38import static java.lang.Integer.signum;
39
40public final class PositionUtils {
41
42    private PositionUtils() {
43        // prevent instantiation
44    }
45
46    public static <T extends Node> void sortByBeginPosition(List<T> nodes) {
47        sortByBeginPosition(nodes, false);
48    }
49
50    public static <T extends Node> void sortByBeginPosition(NodeList<T> nodes) {
51        sortByBeginPosition(nodes, false);
52    }
53
54    public static <T extends Node> void sortByBeginPosition(List<T> nodes, final boolean ignoringAnnotations) {
55        nodes.sort((o1, o2) -> PositionUtils.compare(o1, o2, ignoringAnnotations));
56    }
57
58    public static boolean areInOrder(Node a, Node b) {
59        return areInOrder(a, b, false);
60    }
61
62    public static boolean areInOrder(Node a, Node b, boolean ignoringAnnotations) {
63        return compare(a, b, ignoringAnnotations) <= 0;
64    }
65
66    private static int compare(Node a, Node b, boolean ignoringAnnotations) {
67        if(a.getRange().isPresent() && !b.getRange().isPresent()) {
68            return -1;
69        }
70        if(!a.getRange().isPresent() && b.getRange().isPresent()) {
71            return 1;
72        }
73        if (!a.getRange().isPresent() && !b.getRange().isPresent()) {
74            return 0;
75        }
76        if (ignoringAnnotations) {
77            int signLine = signum(beginLineWithoutConsideringAnnotation(a) - beginLineWithoutConsideringAnnotation(b));
78            if (signLine == 0) {
79                return signum(beginColumnWithoutConsideringAnnotation(a) - beginColumnWithoutConsideringAnnotation(b));
80            } else {
81                return signLine;
82            }
83        }
84
85        Position aBegin = a.getBegin().get();
86        Position bBegin = b.getBegin().get();
87
88        int signLine = signum(aBegin.line - bBegin.line);
89        if (signLine == 0) {
90            return signum(aBegin.column - bBegin.column);
91        } else {
92            return signLine;
93        }
94    }
95
96    public static AnnotationExpr getLastAnnotation(Node node) {
97        if (node instanceof NodeWithAnnotations) {
98            NodeList<AnnotationExpr> annotations = NodeList.nodeList(((NodeWithAnnotations<?>) node).getAnnotations());
99            if (annotations.isEmpty()) {
100                return null;
101            }
102            sortByBeginPosition(annotations);
103            return annotations.get(annotations.size() - 1);
104        } else {
105            return null;
106        }
107    }
108
109    private static int beginLineWithoutConsideringAnnotation(Node node) {
110        return beginNodeWithoutConsideringAnnotations(node).getRange().get().begin.line;
111    }
112
113
114    private static int beginColumnWithoutConsideringAnnotation(Node node) {
115        return beginNodeWithoutConsideringAnnotations(node).getRange().get().begin.column;
116    }
117
118    private static Node beginNodeWithoutConsideringAnnotations(Node node) {
119        if (node instanceof MethodDeclaration || node instanceof FieldDeclaration) {
120            NodeWithType<?, Type> casted = (NodeWithType<?, Type>) node;
121            return casted.getType();
122        } else if (node instanceof ClassOrInterfaceDeclaration) {
123            ClassOrInterfaceDeclaration casted = (ClassOrInterfaceDeclaration) node;
124            return casted.getName();
125        } else {
126            return node;
127        }
128    }
129
130    public static boolean nodeContains(Node container, Node contained, boolean ignoringAnnotations) {
131        final Range containedRange = contained.getRange().get();
132        final Range containerRange = container.getRange().get();
133        if (!ignoringAnnotations || PositionUtils.getLastAnnotation(container) == null) {
134            return container.containsWithin(contained);
135        }
136        if (!container.containsWithin(contained)) {
137            return false;
138        }
139        // if the node is contained, but it comes immediately after the annotations,
140        // let's not consider it contained
141        if (container instanceof NodeWithAnnotations) {
142            int bl = beginLineWithoutConsideringAnnotation(container);
143            int bc = beginColumnWithoutConsideringAnnotation(container);
144            if (bl > containedRange.begin.line) return false;
145            if (bl == containedRange.begin.line && bc > containedRange.begin.column) return false;
146            if (containerRange.end.line < containedRange.end.line) return false;
147            // TODO < or <= ?
148            return !(containerRange.end.line == containedRange.end.line && containerRange.end.column < containedRange.end.column);
149        }
150        return true;
151    }
152
153}
154