1/*
2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24package compiler.compilercontrol.share.method;
25
26import java.lang.reflect.Executable;
27import java.util.Arrays;
28import java.util.stream.Collectors;
29
30/**
31 * Element represents class in method descriptor
32 * that consist from class package and class itself
33 */
34public class ClassType extends MethodElementType {
35    private final String[] packageDirs;
36    private final Class<?> aClass;
37    private boolean setPackage;
38
39    public ClassType(Executable method) {
40        // Use pack/subpack/Class::method separators style
41        super(MethodDescriptor.Separator.SLASH);
42        // Get package
43        aClass = method.getDeclaringClass();
44        Package aPackage = method.getDeclaringClass().getPackage();
45        if (aPackage != null) {
46            // split into directories
47            packageDirs = aPackage.getName().split("\\.");
48        } else {
49            packageDirs = null;
50        }
51        setPackage = true;
52        buildElement(setPackage);
53    }
54
55    @Override
56    public boolean isValid() {
57        if (element.isEmpty()) {
58            return false;
59        }
60        boolean separatorMet = false;
61        char separatorChar = 0;
62        char[] charArray = element.toCharArray();
63        for (int i = 0; i < charArray.length; i++) {
64            char ch = charArray[i];
65            switch (ch) {
66                case '/':
67                case '.':
68                    if (separatorMet) {
69                        if (ch != separatorChar) {
70                            // there are two different separators
71                            return false;
72                        }
73                    } else {
74                        separatorChar = ch;
75                        separatorMet = true;
76                    }
77                    break;
78                case ':':
79                    if (++i != charArray.length) {
80                        if (charArray[i] == ':') {
81                            // :: is invalid separator
82                            separator = MethodDescriptor.Separator.DOUBLECOLON;
83                            return false;
84                        }
85                    }
86                    break;
87                // Invalid separators
88                case ',':
89                case ' ':
90                    return false;
91            }
92        }
93        // set correct separator
94        switch (separatorChar) {
95            case '.':
96                separator = MethodDescriptor.Separator.DOT;
97                break;
98            case '/':
99                separator = MethodDescriptor.Separator.SLASH;
100                break;
101            default:
102                separator = MethodDescriptor.Separator.NONE;
103                break;
104        }
105        return super.isValid();
106    }
107
108    @Override
109    public void setSeparator(MethodDescriptor.Separator separator) {
110        this.separator = separator;
111        buildElement(setPackage);
112    }
113
114    @Override
115    public void setPattern(MethodDescriptor.PatternType patternType) {
116        switch (patternType) {
117            case EXACT:
118                break;
119            case PREFIX:
120                // For prefix pattern use only class name without package
121                buildElement(false);
122                regexp = ".*" + regexp;
123                element = "*" + element;
124                break;
125            case ANY:
126                regexp = ".*";
127                element = "*";
128                break;
129            case SUFFIX:
130                regexp = regexp + ".*";
131                element = element + "*";
132                break;
133            case SUBSTRING:
134                setPattern(MethodDescriptor.PatternType.PREFIX);
135                setPattern(MethodDescriptor.PatternType.SUFFIX);
136                break;
137            default:
138                throw new IllegalArgumentException("ERROR: wrong pattern type "
139                        + patternType);
140        }
141    }
142
143    /**
144     * Builds element string and regexp.
145     *
146     * @param setPackage shows that element should have a package name
147     */
148    private void buildElement(boolean setPackage) {
149        this.setPackage = setPackage;
150        StringBuilder elementBuilder = new StringBuilder();
151        if (packageDirs != null && setPackage) {
152            elementBuilder.append(Arrays.stream(packageDirs)
153                    .collect(Collectors.joining(separator.symbol)));
154            elementBuilder.append(separator.symbol);
155        }
156        String className = aClass.getSimpleName();
157        if (setPackage) {
158            // Add outer classes if any
159            Class<?> enclosingClass = aClass.getEnclosingClass();
160            while (enclosingClass != null) {
161                className = enclosingClass.getSimpleName() + "$" + className;
162                enclosingClass = enclosingClass.getEnclosingClass();
163            }
164        }
165        elementBuilder.append(className);
166        element = elementBuilder.toString();
167        if (separator == MethodDescriptor.Separator.DOT) {
168            // Replace . with / to make regexp look like CommandSignature
169            regexp = element.replace(".", "/");
170        } else {
171            regexp = element;
172        }
173        regexp = regexp.replace("$", "\\$");
174    }
175}
176