001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.harmony.pack200;
018:
019: import java.util.ArrayList;
020:
021: public class IcTuple {
022:
023: public static int NESTED_CLASS_FLAG = 0x00010000;
024: public String C; // this class
025: public int F; // flags
026: public String C2; // outer class
027: public String N; // name
028:
029: private String cachedOuterClassString = null;
030: private String cachedSimpleClassName = null;
031: private boolean initialized = false;
032: private boolean anonymous = false;
033: private boolean member = true;
034:
035: /**
036: * Answer true if the receiver is predicted;
037: * answer false if the receiver is specified
038: * explicitly in the outer and name fields.
039: * @return
040: */
041: public boolean predicted() {
042: return ((F & NESTED_CLASS_FLAG) == 0);
043: }
044:
045: /**
046: * Break the receiver into components at $ boundaries.
047: *
048: * @return
049: */
050: public String[] innerBreakAtDollar(String className) {
051: ArrayList resultList = new ArrayList();
052: int start = 0;
053: int index = 0;
054: while (index < className.length()) {
055: if (className.charAt(index) <= '$') {
056: resultList.add(className.substring(start, index));
057: start = index + 1;
058: }
059: index++;
060: if (index >= className.length()) {
061: // Add the last element
062: resultList.add(className.substring(start, className
063: .length()));
064: }
065: }
066: String[] result = new String[resultList.size()];
067: for (int i = 0; i < resultList.size(); i++) {
068: result[i] = (String) resultList.get(i);
069: }
070: return result;
071: }
072:
073: /**
074: * Answer the outer class name for the receiver.
075: * This may either be specified or inferred from
076: * inner class name.
077: * @return String name of outer class
078: */
079: public String outerClassString() {
080: if (!initialized) {
081: initializeClassStrings();
082: }
083: return cachedOuterClassString;
084: }
085:
086: /**
087: * Answer the inner class name for the receiver.
088: * @return String name of inner class
089: */
090: public String simpleClassName() {
091: if (!initialized) {
092: initializeClassStrings();
093: }
094: return cachedSimpleClassName;
095: }
096:
097: /**
098: * Answer the full name of the inner class represented
099: * by this tuple (including its outer component)
100: * @return String full name of inner class
101: */
102: public String this ClassString() {
103: if (predicted()) {
104: return C;
105: } else {
106: // TODO: this may not be right. What if I
107: // get a class like Foo#Bar$Baz$Bug?
108: return C2 + "$" + N;
109: }
110: }
111:
112: public boolean isMember() {
113: initializeClassStrings();
114: return member;
115: }
116:
117: public boolean isAnonymous() {
118: initializeClassStrings();
119: return anonymous;
120: }
121:
122: private void initializeClassStrings() {
123: if (initialized) {
124: return;
125: }
126: initialized = true;
127: if (!predicted()) {
128: cachedOuterClassString = C2;
129: cachedSimpleClassName = N;
130: return;
131: }
132: // Class names must be calculated from
133: // this class name.
134: String nameComponents[] = innerBreakAtDollar(C);
135: if (nameComponents.length == 0) {
136: throw new Error("Unable to predict outer class name: " + C);
137: }
138: if (nameComponents.length == 1) {
139: throw new Error("Unable to predict inner class name: " + C);
140: }
141: // If we get to this point, nameComponents.length must be >=2
142: int lastPosition = nameComponents.length - 1;
143: cachedSimpleClassName = nameComponents[lastPosition];
144: cachedOuterClassString = new String();
145: for (int index = 0; index < lastPosition; index++) {
146: cachedOuterClassString += nameComponents[index];
147: if (isAllDigits(nameComponents[index])) {
148: member = false;
149: }
150: if (index + 1 != lastPosition) {
151: // TODO: might need more logic to handle
152: // classes with separators of non-$ characters
153: // (ie Foo#Bar)
154: cachedOuterClassString += '$';
155: }
156: }
157: if (isAllDigits(cachedSimpleClassName)) {
158: anonymous = true;
159: member = false;
160: }
161: }
162:
163: private boolean isAllDigits(String nameString) {
164: // Answer true if the receiver is all digits; otherwise answer false.
165: if (null == nameString) {
166: return false;
167: }
168: for (int index = 0; index < nameString.length(); index++) {
169: if (!Character.isDigit(nameString.charAt(index))) {
170: return false;
171: }
172: }
173: return true;
174: }
175:
176: public String toString() {
177: StringBuffer result = new StringBuffer();
178: result.append(this .getClass().getName());
179: result.append('(');
180: result.append(simpleClassName());
181: result.append(" in ");
182: result.append(outerClassString());
183: result.append(')');
184: return result.toString();
185: }
186: }
|