1/*
2 * Copyright (c) 1998, 2014, 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.tools.doclets.internal.toolkit.util;
27
28import java.util.*;
29
30import com.sun.javadoc.*;
31import com.sun.tools.doclets.internal.toolkit.*;
32
33/**
34 * Process and manage grouping of packages, as specified by "-group" option on
35 * the command line.
36 * <p>
37 * For example, if user has used -group option as
38 * -group "Core Packages" "java.*" -group "CORBA Packages" "org.omg.*", then
39 * the packages specified on the command line will be grouped according to their
40 * names starting with either "java." or "org.omg.". All the other packages
41 * which do not fall in the user given groups, are grouped in default group,
42 * named as either "Other Packages" or "Packages" depending upon if "-group"
43 * option used or not at all used respectively.
44 * </p>
45 * <p>
46 * Also the packages are grouped according to the longest possible match of
47 * their names with the grouping information provided. For example, if there
48 * are two groups, like -group "Lang" "java.lang" and -group "Core" "java.*",
49 * will put the package java.lang in the group "Lang" and not in group "Core".
50 * </p>
51 *
52 *  <p><b>This is NOT part of any supported API.
53 *  If you write code that depends on this, you do so at your own risk.
54 *  This code and its internal interfaces are subject to change or
55 *  deletion without notice.</b>
56 *
57 * @author Atul M Dambalkar
58 */
59@Deprecated
60public class Group {
61
62    /**
63     * Map of regular expressions with the corresponding group name.
64     */
65    private Map<String,String> regExpGroupMap = new HashMap<>();
66
67    /**
68     * List of regular expressions sorted according to the length. Regular
69     * expression with longest length will be first in the sorted order.
70     */
71    private List<String> sortedRegExpList = new ArrayList<>();
72
73    /**
74     * List of group names in the same order as given on the command line.
75     */
76    private List<String> groupList = new ArrayList<>();
77
78    /**
79     * Map of non-regular expressions(possible package names) with the
80     * corresponding group name.
81     */
82    private Map<String,String> pkgNameGroupMap = new HashMap<>();
83
84    /**
85     * The global configuration information for this run.
86     */
87    private final Configuration configuration;
88
89    /**
90     * Since we need to sort the keys in the reverse order(longest key first),
91     * the compare method in the implementing class is doing the reverse
92     * comparison.
93     */
94    private static class MapKeyComparator implements Comparator<String> {
95        public int compare(String key1, String key2) {
96            return key2.length() - key1.length();
97        }
98    }
99
100    public Group(Configuration configuration) {
101        this.configuration = configuration;
102    }
103
104    /**
105     * Depending upon the format of the package name provided in the "-group"
106     * option, generate two separate maps. There will be a map for mapping
107     * regular expression(only meta character allowed is '*' and that is at the
108     * end of the regular expression) on to the group name. And another map
109     * for mapping (possible) package names(if the name format doesen't contain
110     * meta character '*', then it is assumed to be a package name) on to the
111     * group name. This will also sort all the regular expressions found in the
112     * reverse order of their lengths, i.e. longest regular expression will be
113     * first in the sorted list.
114     *
115     * @param groupname       The name of the group from -group option.
116     * @param pkgNameFormList List of the package name formats.
117     */
118    public boolean checkPackageGroups(String groupname,
119            String pkgNameFormList) {
120        StringTokenizer strtok = new StringTokenizer(pkgNameFormList, ":");
121        if (groupList.contains(groupname)) {
122            configuration.message.warning("doclet.Groupname_already_used", groupname);
123            return false;
124        }
125        groupList.add(groupname);
126        while (strtok.hasMoreTokens()) {
127            String id = strtok.nextToken();
128            if (id.length() == 0) {
129                configuration.message.warning("doclet.Error_in_packagelist", groupname, pkgNameFormList);
130                return false;
131            }
132            if (id.endsWith("*")) {
133                id = id.substring(0, id.length() - 1);
134                if (foundGroupFormat(regExpGroupMap, id)) {
135                    return false;
136                }
137                regExpGroupMap.put(id, groupname);
138                sortedRegExpList.add(id);
139            } else {
140                if (foundGroupFormat(pkgNameGroupMap, id)) {
141                    return false;
142                }
143                pkgNameGroupMap.put(id, groupname);
144            }
145        }
146        Collections.sort(sortedRegExpList, new MapKeyComparator());
147        return true;
148    }
149
150    /**
151     * Search if the given map has given the package format.
152     *
153     * @param map Map to be searched.
154     * @param pkgFormat The pacakge format to search.
155     *
156     * @return true if package name format found in the map, else false.
157     */
158    boolean foundGroupFormat(Map<String,?> map, String pkgFormat) {
159        if (map.containsKey(pkgFormat)) {
160            configuration.message.error("doclet.Same_package_name_used", pkgFormat);
161            return true;
162        }
163        return false;
164    }
165
166    /**
167     * Group the packages according the grouping information provided on the
168     * command line. Given a list of packages, search each package name in
169     * regular expression map as well as package name map to get the
170     * corresponding group name. Create another map with mapping of group name
171     * to the package list, which will fall under the specified group. If any
172     * package doesen't belong to any specified group on the comamnd line, then
173     * a new group named "Other Packages" will be created for it. If there are
174     * no groups found, in other words if "-group" option is not at all used,
175     * then all the packages will be grouped under group "Packages".
176     *
177     * @param packages Packages specified on the command line.
178     */
179    public Map<String, List<PackageDoc>> groupPackages(Set<PackageDoc> packages) {
180        Map<String,List<PackageDoc>> groupPackageMap = new HashMap<>();
181        String defaultGroupName =
182            (pkgNameGroupMap.isEmpty() && regExpGroupMap.isEmpty())?
183                configuration.message.getText("doclet.Packages") :
184                configuration.message.getText("doclet.Other_Packages");
185        // if the user has not used the default group name, add it
186        if (!groupList.contains(defaultGroupName)) {
187            groupList.add(defaultGroupName);
188        }
189        for (PackageDoc pkg : packages) {
190            String pkgName = pkg.name();
191            String groupName = pkgNameGroupMap.get(pkgName);
192            // if this package is not explicitly assigned to a group,
193            // try matching it to group specified by regular expression
194            if (groupName == null) {
195                groupName = regExpGroupName(pkgName);
196            }
197            // if it is in neither group map, put it in the default
198            // group
199            if (groupName == null) {
200                groupName = defaultGroupName;
201            }
202            getPkgList(groupPackageMap, groupName).add(pkg);
203        }
204        return groupPackageMap;
205    }
206
207    /**
208     * Search for package name in the sorted regular expression
209     * list, if found return the group name.  If not, return null.
210     *
211     * @param pkgName Name of package to be found in the regular
212     * expression list.
213     */
214    String regExpGroupName(String pkgName) {
215        for (String regexp : sortedRegExpList) {
216            if (pkgName.startsWith(regexp)) {
217                return regExpGroupMap.get(regexp);
218            }
219        }
220        return null;
221    }
222
223    /**
224     * For the given group name, return the package list, on which it is mapped.
225     * Create a new list, if not found.
226     *
227     * @param map Map to be searched for gorup name.
228     * @param groupname Group name to search.
229     */
230    List<PackageDoc> getPkgList(Map<String,List<PackageDoc>> map, String groupname) {
231        List<PackageDoc> list = map.get(groupname);
232        if (list == null) {
233            list = new ArrayList<>();
234            map.put(groupname, list);
235        }
236        return list;
237    }
238
239    /**
240     * Return the list of groups, in the same order as specified
241     * on the command line.
242     */
243    public List<String> getGroupList() {
244        return groupList;
245    }
246}
247