2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
package org.netbeans.modules.apisupport.project.universe;
44
import java.io.DataInput;
45
import java.io.DataInputStream;
47
import java.io.IOException;
48
import java.io.InputStream;
49
import java.util.ArrayList;
50
import java.util.Arrays;
51
import java.util.Collections;
52
import java.util.Enumeration;
53
import java.util.HashSet;
54
import java.util.Iterator;
55
import java.util.List;
57
import java.util.StringTokenizer;
58
import java.util.jar.JarEntry;
59
import java.util.jar.JarFile;
60
import org.netbeans.modules.apisupport.project.ManifestManager;
61
import org.netbeans.modules.apisupport.project.Util;
62
import org.netbeans.spi.project.support.ant.PropertyUtils;
63
import org.openide.ErrorManager;
65
abstract class AbstractEntry implements ModuleEntry {
67
private String localizedName;
68
private Set<String> publicClassNames;
70
protected abstract LocalizedBundleInfo getBundleInfo();
72
public String getLocalizedName() {
73
if (localizedName == null) {
74
localizedName = getBundleInfo().getDisplayName();
75
if (localizedName == null) {
76
localizedName = getCodeNameBase();
82
public String getCategory() {
83
return getBundleInfo().getCategory();
86
public String getShortDescription() {
87
return getBundleInfo().getShortDescription();
90
public String getLongDescription() {
91
return getBundleInfo().getLongDescription();
94
public int compareTo(Object o) {
95
int retval = getLocalizedName().compareTo(((ModuleEntry) o).getLocalizedName());
96
return (retval != 0) ? retval : getCodeNameBase().compareTo(((ModuleEntry) o).getCodeNameBase());
99
public synchronized Set<String> getPublicClassNames() {
100
if (publicClassNames == null) {
102
publicClassNames = computePublicClassNamesInMainModule();
103
String[] cpext = PropertyUtils.tokenizePath(getClassPathExtensions());
104
for (int i = 0; i < cpext.length; i++) {
105
File ext = new File(cpext[i]);
107
Util.err.log(ErrorManager.WARNING, "Could not find Class-Path extension " + ext + " of " + this);
110
scanJarForPublicClassNames(publicClassNames, ext);
112
} catch (IOException e) {
113
publicClassNames = Collections.emptySet();
114
Util.err.annotate(e, ErrorManager.UNKNOWN, "While scanning for public classes in " + this, null, null, null); // NOI18N
115
Util.err.notify(ErrorManager.INFORMATIONAL, e);
118
return publicClassNames;
121
protected final void scanJarForPublicClassNames(Set<String> result, File jar) throws IOException {
122
Set<String> publicPackagesSlashNonRec = new HashSet<String>();
123
List<String> publicPackagesSlashRec = new ArrayList<String>();
124
for (ManifestManager.PackageExport pkg : getPublicPackages()) {
125
String name = pkg.getPackage().replace('.', '/') + '/';
126
if (pkg.isRecursive()) {
127
publicPackagesSlashRec.add(name);
129
publicPackagesSlashNonRec.add(name);
132
JarFile jf = new JarFile(jar);
134
Enumeration entries = jf.entries();
135
ENTRY: while (entries.hasMoreElements()) {
136
JarEntry entry = (JarEntry) entries.nextElement();
137
String path = entry.getName();
138
if (!path.endsWith(".class")) { // NOI18N
141
int slash = path.lastIndexOf('/');
145
String pkg = path.substring(0, slash + 1);
146
if (!publicPackagesSlashNonRec.contains(pkg)) {
148
Iterator it = publicPackagesSlashRec.iterator();
149
while (it.hasNext()) {
150
if (pkg.startsWith((String) it.next())) {
159
StringTokenizer tok = new StringTokenizer(path, "$"); // NOI18N
160
while (tok.hasMoreTokens()) {
161
String component = tok.nextToken();
162
char c = component.charAt(0);
163
if (c >= '0' && c <= '9') {
164
// Generated anon inner class name, skip.
168
if (!isPublic(jf, entry)) {
171
result.add(path.substring(0, path.length() - 6).replace('/', '.'));
178
protected abstract Set<String> computePublicClassNamesInMainModule() throws IOException;
180
// XXX consider inheritance refactoring instead.
182
* Just a convenient methods. <code>null</code> may be passed as a
183
* <cdoe>friends</code>.
185
protected static boolean isDeclaredAsFriend(String[] friends, String cnb) {
186
return friends == null ? true : Arrays.binarySearch(friends, cnb) >= 0;
189
/** Checks whether a .class file is marked as public or not. */
190
private static boolean isPublic(JarFile jf, JarEntry entry) throws IOException {
191
InputStream is = jf.getInputStream(entry);
193
DataInput input = new DataInputStream(is);
194
skip(input, 8); // magic, minor_version, major_version
195
// Have to partially parse constant pool to skip over it:
196
int size = input.readUnsignedShort() - 1; // constantPoolCount
197
for (int i = 0; i < size; i++) {
198
byte tag = input.readByte();
200
case 1: // CONSTANT_Utf8
203
case 3: // CONSTANT_Integer
204
case 4: // CONSTANT_Float
205
case 9: // CONSTANT_Fieldref
206
case 10: // CONSTANT_Methodref
207
case 11: // CONSTANT_InterfaceMethodref
208
case 12: // CONSTANT_NameAndType
211
case 7: // CONSTANT_Class
212
case 8: // CONSTANT_String
215
case 5: // CONSTANT_Long
216
case 6: // CONSTANT_Double
218
i++; // weirdness in spec
221
throw new IOException("Unrecognized constant pool tag " + tag + " at index " + i); // NOI18N
224
int accessFlags = input.readUnsignedShort();
225
return (accessFlags & 0x0001) > 0;
230
private static void skip(DataInput input, int bytes) throws IOException {
231
int skipped = input.skipBytes(bytes);
232
if (skipped != bytes) {
233
throw new IOException();