1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21package proguard;
22
23import java.io.*;
24import java.util.*;
25
26/**
27 * This class checks and prints out information about the GPL.
28 *
29 * @author Eric Lafortune
30 */
31public class GPL
32{
33    /**
34     * Prints out a note about the GPL if ProGuard is linked against unknown
35     * code.
36     */
37    public static void check()
38    {
39        ByteArrayOutputStream out = new ByteArrayOutputStream();
40        new Exception().printStackTrace(new PrintStream(out));
41        LineNumberReader reader = new LineNumberReader(
42                                  new InputStreamReader(
43                                  new ByteArrayInputStream(out.toByteArray())));
44
45        Set unknownPackageNames = unknownPackageNames(reader);
46
47        if (unknownPackageNames.size() > 0)
48        {
49            String uniquePackageNames = uniquePackageNames(unknownPackageNames);
50
51            System.out.println("ProGuard is released under the GNU General Public License. The authors of all");
52            System.out.println("programs or plugins that link to it ("+uniquePackageNames+"...) therefore");
53            System.out.println("must ensure that these programs carry the GNU General Public License as well.");
54        }
55    }
56
57
58    /**
59     * Returns a set of package names from the given stack trace.
60     */
61    private static Set unknownPackageNames(LineNumberReader reader)
62    {
63        Set packageNames = new HashSet();
64
65        try
66        {
67            while (true)
68            {
69                String line = reader.readLine();
70                if (line == null)
71                {
72                    break;
73                }
74
75                line = line.trim();
76                if (line.startsWith("at "))
77                {
78                    line = line.substring(2).trim();
79                    line = trimSuffix(line, '(');
80                    line = trimSuffix(line, '.');
81                    line = trimSuffix(line, '.');
82
83                    if (line.length() > 0 && !isKnown(line))
84                    {
85                        packageNames.add(line);
86                    }
87                }
88            }
89        }
90        catch (IOException ex)
91        {
92            // We'll just stop looking for more names.
93        }
94
95        return packageNames;
96    }
97
98
99    /**
100     * Returns a comma-separated list of package names from the set, excluding
101     * any subpackages of packages in the set.
102     */
103    private static String uniquePackageNames(Set packageNames)
104    {
105        StringBuffer buffer = new StringBuffer();
106
107        Iterator iterator = packageNames.iterator();
108        while (iterator.hasNext())
109        {
110            String packageName = (String)iterator.next();
111            if (!containsPrefix(packageNames, packageName))
112            {
113                buffer.append(packageName).append(", ");
114            }
115        }
116
117        return buffer.toString();
118    }
119
120
121    /**
122     * Returns a given string without the suffix, as defined by the given
123     * separator.
124     */
125    private static String trimSuffix(String string, char separator)
126    {
127        int index = string.lastIndexOf(separator);
128        return index < 0 ? "" : string.substring(0, index);
129    }
130
131
132    /**
133     * Returns whether the given set contains a prefix of the given name.
134     */
135    private static boolean containsPrefix(Set set, String name)
136    {
137        int index = 0;
138
139        while (!set.contains(name.substring(0, index)))
140        {
141            index = name.indexOf('.', index + 1);
142            if (index < 0)
143            {
144                return false;
145            }
146        }
147
148        return true;
149    }
150
151
152    /**
153     * Returns whether the given package name has been granted an exception
154     * against the GPL linking clause, by the copyright holder of ProGuard.
155     * This method is not legally binding, but of course the actual license is.
156     * Please contact the copyright holder if you would like an exception for
157     * your code as well.
158     */
159    private static boolean isKnown(String packageName)
160    {
161        return packageName.startsWith("java")                   ||
162               packageName.startsWith("sun.reflect")            ||
163               packageName.startsWith("proguard")               ||
164               packageName.startsWith("org.apache.tools.ant")   ||
165               packageName.startsWith("org.apache.tools.maven") ||
166               packageName.startsWith("org.eclipse")            ||
167               packageName.startsWith("org.netbeans")           ||
168               packageName.startsWith("com.sun.kvem")           ||
169               packageName.startsWith("net.certiv.proguarddt")  ||
170               packageName.startsWith("eclipseme")              ||
171               packageName.startsWith("jg.j2me")                ||
172               packageName.startsWith("jg.common")              ||
173               packageName.startsWith("jg.buildengine");
174    }
175
176
177    public static void main(String[] args)
178    {
179        LineNumberReader reader = new LineNumberReader(
180                                  new InputStreamReader(System.in));
181
182        Set unknownPackageNames = unknownPackageNames(reader);
183
184        if (unknownPackageNames.size() > 0)
185        {
186            String uniquePackageNames = uniquePackageNames(unknownPackageNames);
187
188            System.out.println(uniquePackageNames);
189        }
190    }
191}
192