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.security;
19
20import java.io.IOException;
21import java.io.Serializable;
22
23import org.apache.harmony.security.internal.nls.Messages;
24
25/**
26 * {@code BasicPermission} is the common base class of all permissions which
27 * have a name but no action lists. A {@code BasicPermission} is granted or it
28 * is not.
29 * <p>
30 * Names of a BasicPermission follow the dot separated, hierarchical property
31 * naming convention. Asterisk '*' can be used as wildcards. Either by itself,
32 * matching anything, or at the end of the name, immediately preceded by a '.'.
33 * For example:
34 *
35 * <pre>
36 * java.io.*  grants all permissions under the java.io permission hierarchy
37 * *          grants all permissions
38 * </pre>
39 * <p>
40 * While this class ignores the action list in the
41 * {@link #BasicPermission(String, String)} constructor, subclasses may
42 * implement actions on top of this class.
43 */
44public abstract class BasicPermission extends Permission implements
45    Serializable {
46
47    private static final long serialVersionUID = 6279438298436773498L;
48
49    /**
50     * Constructs a new instance of {@code BasicPermission} with the specified
51     * name.
52     *
53     * @param name
54     *            the name of the permission.
55     * @throws NullPointerException if {@code name} is {@code null}.
56     * @throws IllegalArgumentException if {@code name.length() == 0}.
57     */
58    public BasicPermission(String name) {
59        super(name);
60        checkName(name);
61    }
62
63    /**
64     * Constructs a new instance of {@code BasicPermission} with the specified
65     * name. The {@code action} parameter is ignored.
66     *
67     * @param name
68     *            the name of the permission.
69     * @param action
70     *            is ignored.
71     * @throws NullPointerException
72     *             if {@code name} is {@code null}.
73     * @throws IllegalArgumentException
74     *             if {@code name.length() == 0}.
75     */
76    public BasicPermission(String name, String action) {
77        super(name);
78        checkName(name);
79    }
80
81    /**
82     * Checks name parameter
83     */
84    private final void checkName(String name) {
85        if (name == null) {
86            throw new NullPointerException(Messages.getString("security.28")); //$NON-NLS-1$
87        }
88        if (name.length() == 0) {
89            throw new IllegalArgumentException(Messages.getString("security.29")); //$NON-NLS-1$
90        }
91    }
92
93    /**
94     * Compares the specified object with this {@code BasicPermission} for
95     * equality. Returns {@code true} if the specified object has the same class
96     * and the two {@code Permissions}s have the same name.
97     * <p>
98     * The {@link #implies(Permission)} method should be used for making access
99     * control checks.
100     *
101     * @param obj
102     *            object to be compared for equality with this {@code
103     *            BasicPermission}.
104     * @return {@code true} if the specified object is equal to this {@code
105     *         BasicPermission}, otherwise {@code false}.
106     */
107    @Override
108    public boolean equals(Object obj) {
109        if (obj == this) {
110            return true;
111        }
112
113        if (obj != null && obj.getClass() == this.getClass()) {
114            return this.getName().equals(((Permission)obj).getName());
115        }
116        return false;
117    }
118
119    /**
120     * Returns the hash code value for this {@code BasicPermission}. Returns the
121     * same hash code for {@code BasicPermission}s that are equal to each other
122     * as required by the general contract of {@link Object#hashCode}.
123     *
124     * @return the hash code value for this {@code BasicPermission}.
125     * @see Object#equals(Object)
126     * @see BasicPermission#equals(Object)
127     */
128    @Override
129    public int hashCode() {
130        return getName().hashCode();
131    }
132
133    /**
134     * Returns the actions associated with this permission. Since {@code
135     * BasicPermission} instances have no actions, an empty string is returned.
136     *
137     * @return an empty string.
138     */
139    @Override
140    public String getActions() {
141        return ""; //$NON-NLS-1$
142    }
143
144    /**
145     * Indicates whether the specified permission is implied by this permission.
146     *
147     * @param permission
148     *            the permission to check against this permission.
149     * @return {@code true} if the specified permission is implied by this
150     *         permission, {@code false} otherwise.
151     */
152    @Override
153    public boolean implies(Permission permission) {
154        if (permission != null && permission.getClass() == this.getClass()) {
155            return nameImplies(getName(), permission.getName());
156        }
157        return false;
158    }
159
160    /**
161     * Checks if {@code thisName} implies {@code thatName},
162     * accordingly to hierarchical property naming convention.
163     * It is assumed that names cannot be {@code null} or empty.
164     */
165    static boolean nameImplies(String thisName, String thatName) {
166        if (thisName == thatName) {
167            return true;
168        }
169        int end = thisName.length();
170        if (end > thatName.length()) {
171            return false;
172        }
173        if (thisName.charAt(--end) == '*'
174            && (end == 0 || thisName.charAt(end - 1) == '.')) {
175            //wildcard found
176            end--;
177        } else if (end != (thatName.length()-1)) {
178            //names are not equal
179            return false;
180        }
181        for (int i = end; i >= 0; i--) {
182            if (thisName.charAt(i) != thatName.charAt(i)) {
183                return false;
184            }
185        }
186        return true;
187    }
188
189    /**
190     * Returns an empty {@link PermissionCollection} for holding permissions.
191     * <p>
192     * For {@code PermissionCollection} (and subclasses which do not override
193     * this method), the collection which is returned does <em>not</em> invoke
194     * the {@link #implies(Permission)} method of the permissions which are
195     * stored in it when checking if the collection implies a permission.
196     * Instead, it assumes that if the type of the permission is correct, and
197     * the name of the permission is correct, there is a match.
198     *
199     * @return an empty {@link PermissionCollection} for holding permissions.
200     * @see BasicPermissionCollection
201     */
202    @Override
203    public PermissionCollection newPermissionCollection() {
204        return new BasicPermissionCollection();
205    }
206
207    /**
208     * Checks name after default deserialization.
209     */
210    private void readObject(java.io.ObjectInputStream in) throws IOException,
211        ClassNotFoundException {
212        in.defaultReadObject();
213        checkName(this.getName());
214    }
215}
216