1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package java.lang;
19
20import java.io.Serializable;
21
22/**
23 * A representation of a single stack frame. Arrays of {@code StackTraceElement}
24 * are stored in {@link Throwable} objects to represent the whole state of the
25 * call stack at the time a {@code Throwable} gets thrown.
26 *
27 * @see Throwable#getStackTrace()
28 */
29public final class StackTraceElement implements Serializable {
30
31    private static final long serialVersionUID = 6992337162326171013L;
32
33    private static final int NATIVE_LINE_NUMBER = -2;
34
35    String declaringClass;
36
37    String methodName;
38
39    String fileName;
40
41    int lineNumber;
42
43    /**
44     * Constructs a new {@code StackTraceElement} for a specified execution
45     * point.
46     *
47     * @param cls
48     *            the fully qualified name of the class where execution is at.
49     * @param method
50     *            the name of the method where execution is at.
51     * @param file
52     *            The name of the file where execution is at or {@code null}.
53     * @param line
54     *            the line of the file where execution is at, a negative number
55     *            if unknown or {@code -2} if the execution is in a native
56     *            method.
57     * @throws NullPointerException
58     *             if {@code cls} or {@code method} is {@code null}.
59     */
60    public StackTraceElement(String cls, String method, String file, int line) {
61        if (cls == null) {
62            throw new NullPointerException("cls == null");
63        } else if (method == null) {
64            throw new NullPointerException("method == null");
65        }
66        declaringClass = cls;
67        methodName = method;
68        fileName = file;
69        lineNumber = line;
70    }
71
72    /**
73     * <p>
74     * Private, nullary constructor for VM use only.
75     * </p>
76     */
77    private StackTraceElement() {
78    }
79
80    /**
81     * Compares this instance with the specified object and indicates if they
82     * are equal. In order to be equal, the following conditions must be
83     * fulfilled:
84     * <ul>
85     * <li>{@code obj} must be a stack trace element,</li>
86     * <li>the method names of this stack trace element and of {@code obj} must
87     * not be {@code null},</li>
88     * <li>the class, method and file names as well as the line number of this
89     * stack trace element and of {@code obj} must be equal.</li>
90     * </ul>
91     *
92     * @param obj
93     *            the object to compare this instance with.
94     * @return {@code true} if the specified object is equal to this
95     *         {@code StackTraceElement}; {@code false} otherwise.
96     * @see #hashCode
97     */
98    @Override
99    public boolean equals(Object obj) {
100        if (!(obj instanceof StackTraceElement)) {
101            return false;
102        }
103        StackTraceElement castObj = (StackTraceElement) obj;
104
105        /*
106         * Unknown methods are never equal to anything (not strictly to spec,
107         * but spec does not allow null method/class names)
108         */
109        if ((methodName == null) || (castObj.methodName == null)) {
110            return false;
111        }
112
113        if (!getMethodName().equals(castObj.getMethodName())) {
114            return false;
115        }
116        if (!getClassName().equals(castObj.getClassName())) {
117            return false;
118        }
119        String localFileName = getFileName();
120        if (localFileName == null) {
121            if (castObj.getFileName() != null) {
122                return false;
123            }
124        } else {
125            if (!localFileName.equals(castObj.getFileName())) {
126                return false;
127            }
128        }
129        if (getLineNumber() != castObj.getLineNumber()) {
130            return false;
131        }
132
133        return true;
134    }
135
136    /**
137     * Returns the fully qualified name of the class belonging to this
138     * {@code StackTraceElement}.
139     *
140     * @return the fully qualified type name of the class
141     */
142    public String getClassName() {
143        return (declaringClass == null) ? "<unknown class>" : declaringClass;
144    }
145
146    /**
147     * Returns the name of the Java source file containing class belonging to
148     * this {@code StackTraceElement}.
149     *
150     * @return the name of the file, or {@code null} if this information is not
151     *         available.
152     */
153    public String getFileName() {
154        return fileName;
155    }
156
157    /**
158     * Returns the line number in the source for the class belonging to this
159     * {@code StackTraceElement}.
160     *
161     * @return the line number, or a negative number if this information is not
162     *         available.
163     */
164    public int getLineNumber() {
165        return lineNumber;
166    }
167
168    /**
169     * Returns the name of the method belonging to this {@code
170     * StackTraceElement}.
171     *
172     * @return the name of the method, or "<unknown method>" if this information
173     *         is not available.
174     */
175    public String getMethodName() {
176        return (methodName == null) ? "<unknown method>" : methodName;
177    }
178
179    @Override
180    public int hashCode() {
181        /*
182         * Either both methodName and declaringClass are null, or neither are
183         * null.
184         */
185        if (methodName == null) {
186            // all unknown methods hash the same
187            return 0;
188        }
189        // declaringClass never null if methodName is non-null
190        return methodName.hashCode() ^ declaringClass.hashCode();
191    }
192
193    /**
194     * Indicates if the method name returned by {@link #getMethodName()} is
195     * implemented as a native method.
196     *
197     * @return {@code true} if the method in which this stack trace element is
198     *         executing is a native method; {@code false} otherwise.
199     */
200    public boolean isNativeMethod() {
201        return lineNumber == NATIVE_LINE_NUMBER;
202    }
203
204    @Override
205    public String toString() {
206        StringBuilder buf = new StringBuilder(80);
207
208        buf.append(getClassName());
209        buf.append('.');
210        buf.append(getMethodName());
211
212        if (isNativeMethod()) {
213            buf.append("(Native Method)");
214        } else {
215            String fName = getFileName();
216
217            if (fName == null) {
218                buf.append("(Unknown Source)");
219            } else {
220                int lineNum = getLineNumber();
221
222                buf.append('(');
223                buf.append(fName);
224                if (lineNum >= 0) {
225                    buf.append(':');
226                    buf.append(lineNum);
227                }
228                buf.append(')');
229            }
230        }
231        return buf.toString();
232    }
233}
234