/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.jini.security;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.security.AllPermission;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.UnresolvedPermission;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import net.jini.security.policy.DynamicPolicy;
/**
* Permission required to dynamically grant permissions by security policy
* providers which implement the {@link DynamicPolicy} interface. Each
* <code>GrantPermission</code> instance contains a set of permissions that can
* be granted by code authorized with the <code>GrantPermission</code>. When
* the {@link DynamicPolicy#grant DynamicPolicy.grant} method is invoked, the
* <code>checkPermission</code> method of the installed security manager (if
* any) is called with a <code>GrantPermission</code> containing the
* permissions to grant; if the calling context does not have any permissions
* which imply the <code>GrantPermission</code>, then the grant operation will
* fail.
* <p>
* In addition to authorizing granting of contained permissions, each
* <code>GrantPermission</code> also authorizes granting of
* <code>GrantPermission</code>s for contained permissions, as well as granting
* of permissions contained within nested <code>GrantPermission</code>s. For
* example, if <code>GrantPermission g1</code> contains <code>Permission
* p</code>, <code>g1</code> authorizes granting of both <code>p</code> and
* <code>GrantPermission(p)</code>; if <code>GrantPermission g2</code> contains
* <code>GrantPermission(p)</code>, then <code>g2</code> also authorizes
* granting of both <code>p</code> and <code>GrantPermission(p)</code>.
* <p>
* The name (also referred to as the "target name") of each
* <code>GrantPermission</code> instance carries a string representation of the
* permissions contained by the <code>GrantPermission</code>, while the actions
* string of each <code>GrantPermission</code> is always the empty string. If
* a <code>GrantPermission</code> is serialized, only its name string is sent
* (i.e., contained permissions are not themselves serialized). Upon
* deserialization, the set of contained permissions is reconstituted based on
* information in the name string. <code>GrantPermission</code>s constructed
* explicitly with {@link UnresolvedPermission}s (through either the {@link
* #GrantPermission(Permission)} or {@link #GrantPermission(Permission[])}
* constructor) will have incomplete target names that cannot be used to
* instantiate other <code>GrantPermission</code>s, and will not be
* serializable--attempting to serialize such a <code>GrantPermission</code>
* will cause a <code>java.io.NotSerializableException</code> to be thrown.
* <p>
* The syntax of the target name approximates that used for specifying
* permissions in the default security policy file; it is listed below using
* the same grammar notation employed by <i>The Java(TM) Language
* Specification</i>:
* <pre>
* <i>Target</i>:
* <i>DelimiterDeclaration</i><sub>opt</sub> <i>Permissions</i> ;<sub>opt</sub>
*
* <i>DelimiterDeclaration</i>:
* delim = <i>DelimiterCharacter</i>
*
* <i>Permissions</i>:
* <i>Permission</i>
* <i>Permissions</i> ; <i>Permission</i>
*
* <i>Permission</i>:
* <i>PermissionClassName</i>
* <i>PermissionClassName Name</i>
* <i>PermissionClassName Name</i> , <i>Actions</i>
*
* <i>PermissionClassName</i>:
* <i>ClassName</i>
*
* <i>Name</i>:
* <i>DelimitedString</i>
*
* <i>Actions</i>:
* <i>DelimitedString</i>
* </pre>
* The production for <i>ClassName</i> is the same as that used in <i>The
* Java Language Specification</i>. <i>DelimiterCharacter</i> can be any
* unquoted non-whitespace character other than ';' (single and
* double-quote characters themselves are allowed). If
* <i>DelimiterCharacter</i> is not specified, then the double-quote
* character is the default delimiter. <i>DelimitedString</i> is the same
* as the <i>StringLiteral</i> production in <i>The Java Language
* Specification</i>, except that it is delimited by the
* <i>DelimiterDeclaration</i>-specified (or default) delimiter character
* instead of the double-quote character exclusively.
* <p>
* Note that if the double-quote character is used as the delimiter and the
* name or actions strings of specified permissions themselves contain nested
* double-quote characters, then those characters must be escaped (or in some
* cases doubly-escaped) appropriately. For example, the following policy file
* entry would yield a <code>GrantPermission</code> containing a
* <code>FooPermission</code> in which the target name would include the word
* "quoted" surrounded by double-quote characters:
* <pre>
* permission net.jini.security.GrantPermission
* "FooPermission \"a \\\"quoted\\\" string\"";
* </pre>
* For comparison, the following policy file entry which uses a custom
* delimiter would yield an equivalent <code>GrantPermission</code>:
* <pre>
* permission net.jini.security.GrantPermission
* "delim=| FooPermission |a \"quoted\" string|";
* </pre>
* Some additional example policy file permissions:
* <pre>
* // allow granting of permission to listen for and accept connections
* permission net.jini.security.GrantPermission
* "java.net.SocketPermission \"localhost:1024-\", \"accept,listen\"";
*
* // allow granting of permissions to read files under /foo, /bar directories
* permission net.jini.security.GrantPermission
* "delim=' java.io.FilePermission '/foo/-', 'read'; java.io.FilePermission '/bar/-', 'read'";
*
* // allow granting of permission for client authentication as jack, with or without delegation, to any server
* permission net.jini.security.GrantPermission
* "delim=| net.jini.security.AuthenticationPermission |javax.security.auth.x500.X500Principal \"CN=jack\"|, |delegate|";
* </pre>
*
* @author Sun Microsystems, Inc.
* @see DynamicPolicy#grant(Class, Principal[], Permission[])
* @since 2.0
*/
public final class GrantPermission extends Permission {
private static final long serialVersionUID = 4668259055340724280L;
private static final Class[] PARAMS0 = {};
private static final Class[] PARAMS1 = { String.class };
private static final Class[] PARAMS2 = { String.class, String.class };
private transient Permission[] grants;
private transient boolean unserializable;
private transient volatile Implier implier;
private transient volatile Integer hash;
/**
* Creates a <code>GrantPermission</code> for the permission(s) specified
* in the name string.
*
* @param name string describing contained permissions
* @throws NullPointerException if <code>name</code> is <code>null</code>
* @throws IllegalArgumentException if unable to parse target name
*/
public GrantPermission(String name) {
super(name);
initFromName(name);
}
/**
* Creates a <code>GrantPermission</code> for the given permission.
*
* @param permission permission to allow to be granted
* @throws NullPointerException if <code>permission</code> is
* <code>null</code>
*/
public GrantPermission(Permission permission) {
this(new Permission[]{ permission });
}
/**
* Creates a <code>GrantPermission</code> for the given permissions. The
* permissions array passed in is neither modified nor retained; subsequent
* changes to the array have no effect on the <code>GrantPermission</code>.
*
* @param permissions permissions to allow to be granted
* @throws NullPointerException if <code>permissions</code> array or any
* element of <code>permissions</code> array is <code>null</code>
*/
public GrantPermission(Permission[] permissions) {
super(constructName(permissions = (Permission[]) permissions.clone()));
grants = flatten(permissions);
for (int i = 0; i < permissions.length; i++) {
if (permissions[i] instanceof UnresolvedPermission) {
unserializable = true;
return;
}
}
}
/**
* Returns canonical string representation of this permission's actions,
* which for <code>GrantPermission</code> is always the empty string
* <code>""</code>.
*
* @return the empty string <code>""</code>
*/
public String getActions() {
return "";
}
/**
* Returns a newly created empty mutable permission collection for
* <code>GrantPermission</code> instances. The <code>implies</code> method
* of the returned <code>PermissionCollection</code> instance is defined as
* follows: for a given <code>GrantPermission g</code>, let
* <code>c(g)</code> denote the set of all permissions contained within
* <code>g</code> or within arbitrarily nested
* <code>GrantPermission</code>s inside <code>g</code>, excluding nested
* <code>GrantPermission</code>s themselves. Then, a <code>GrantPermission
* g</code> is implied by the <code>PermissionCollection pc</code> if and
* only if each permission in <code>c(g)</code> is implied by the union of
* <code>c(p)</code> for all <code>p</code> in <code>pc</code>.
* <p>
* Implication of contained
* <code>java.security.UnresolvedPermission</code>s is special-cased: an
* <code>UnresolvedPermission p1</code> is taken to imply another
* <code>UnresolvedPermission p2</code> if and only if the serialized
* representations of <code>p1</code> and <code>p2</code> are identical.
*
* @return newly created empty mutable permission collection for
* <code>GrantPermissions</code>
*/
public PermissionCollection newPermissionCollection() {
return new GrantPermissionCollection();
}
/**
* Returns <code>true</code> if the given permission is a
* <code>GrantPermission</code> implied by this permission, or
* <code>false</code> otherwise. Implication is defined as follows: for a
* given <code>GrantPermission g</code>, let <code>c(g)</code> denote the
* set of all permissions contained within <code>g</code> or within
* arbitrarily nested <code>GrantPermission</code>s inside <code>g</code>,
* excluding nested <code>GrantPermission</code>s themselves. Then, a
* <code>GrantPermission g1</code> is implied by another
* <code>GrantPermission g2</code> if and only if each permission in
* <code>c(g1)</code> is implied by <code>c(g2)</code>.
* <p>
* Implication of contained
* <code>java.security.UnresolvedPermission</code>s is special-cased: an
* <code>UnresolvedPermission p1</code> is taken to imply another
* <code>UnresolvedPermission p2</code> if and only if the serialized
* representations of <code>p1</code> and <code>p2</code> are identical.
*
* @param permission permission to check
* @return <code>true</code> if given permission is implied by this
* permission, <code>false</code> otherwise
*/
public boolean implies(Permission permission) {
if (!(permission instanceof GrantPermission)) {
return false;
}
// perm -> perm implies infrequent, so construct implier lazily
if (implier == null) {
Implier imp = new Implier();
imp.add(this);
implier = imp;
}
return implier.implies(permission);
}
/**
* Returns <code>true</code> if the given object is a
* <code>GrantPermission</code> which both implies and is implied by this
* permission; returns <code>false</code> otherwise.
*
* @param obj object to compare against
* @return <code>true</code> if given object is a
* <code>GrantPermission</code> which both implies and is implied
* by this permission, <code>false</code> otherwise
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof GrantPermission) {
GrantPermission gp = (GrantPermission) obj;
return this.implies(gp) && gp.implies(this);
}
return false;
}
public int hashCode() {
if (hash == null) {
hash = new Integer(computeHashCode());
}
return hash.intValue();
}
/**
* Returns hash code computed by summing hash codes of each distinct
* permission class name.
*/
private int computeHashCode() {
int sum = 0;
HashSet set = new HashSet(grants.length);
for (int i = 0; i < grants.length; i++) {
Permission p = grants[i];
String pcn = p.getClass().getName();
if (p instanceof AllPermission) {
return pcn.hashCode();
} else if (p instanceof UnresolvedPermission) {
pcn += ":" + p.getName(); // add name of unresolved class
}
if (!set.contains(pcn)) {
set.add(pcn);
sum += pcn.hashCode();
}
}
return sum;
}
/**
* Writes target name representing contained permissions.
*
* @throws NotSerializableException if the <code>GrantPermission</code>
* was constructed explicitly with
* <code>java.security.UnresolvedPermission</code>s
*/
private void writeObject(ObjectOutputStream out) throws IOException {
if (unserializable) {
throw new NotSerializableException(
GrantPermission.class.getName());
}
out.defaultWriteObject();
}
/**
* Reconstitutes contained permissions based on the information in the
* target name.
*
* @throws InvalidObjectException if the target name is <code>null</code>
* or does not conform to the syntax specified in the
* documentation for {@link GrantPermission}
*/
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException
{
in.defaultReadObject();
try {
initFromName(getName());
} catch (RuntimeException e) {
if (e instanceof NullPointerException ||
e instanceof IllegalArgumentException)
{
InvalidObjectException ee =
new InvalidObjectException(e.getMessage());
ee.initCause(e);
throw ee;
}
throw e;
}
}
/**
* Initializes GrantPermission to contain permissions described in the
* given name. Throws an IllegalArgumentException if the name is
* misformatted, or specifies an invalid permission class. Throws a
* SecurityException if access to the class is not permitted.
*/
private void initFromName(String name) {
PermissionInfo[] pia = parsePermissions(name);
ArrayList l = new ArrayList();
for (int i = 0; i < pia.length; i++) {
PermissionInfo pi = pia[i];
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
int d = pi.type.lastIndexOf('.');
if (d != -1) {
sm.checkPackageAccess(pi.type.substring(0, d));
}
}
Class cl;
try {
cl = Class.forName(pi.type);
} catch (ClassNotFoundException ex) {
l.add(new UnresolvedPermission(
pi.type, pi.name, pi.actions, null));
continue;
}
if (!Permission.class.isAssignableFrom(cl)) {
throw new IllegalArgumentException(
"not a permission class: " + cl);
}
if (!Modifier.isPublic(cl.getModifiers())) {
throw new IllegalArgumentException(
"non-public permission class: " + cl);
}
if (pi.name == null) {
try {
Constructor c = cl.getConstructor(PARAMS0);
l.add(c.newInstance(new Object[0]));
continue;
} catch (Exception ex) {
}
}
if (pi.actions == null) {
try {
Constructor c = cl.getConstructor(PARAMS1);
l.add(c.newInstance(new Object[]{ pi.name }));
continue;
} catch (Exception ex) {
}
}
try {
Constructor c = cl.getConstructor(PARAMS2);
l.add(c.newInstance(new Object[]{ pi.name, pi.actions }));
continue;
} catch (Exception ex) {
}
throw new IllegalArgumentException(
"uninstantiable permission class: " + cl);
}
grants = flatten((Permission[]) l.toArray(new Permission[l.size()]));
}
/**
* Parses permission information from given GrantPermission name string.
* Throws an IllegalArgumentException if the name string is misformatted.
*/
private static PermissionInfo[] parsePermissions(String s) {
try {
ArrayList l = new ArrayList();
StreamTokenizer st = createTokenizer(s);
char delim = '"';
if (st.nextToken() == StreamTokenizer.TT_WORD &&
st.sval.equals("delim"))
{
if (st.nextToken() == '=') {
if (st.nextToken() == StreamTokenizer.TT_WORD) {
if (st.sval.length() > 1) {
throw new IllegalArgumentException(
"excess delimiter characters");
}
delim = st.sval.charAt(0);
} else {
delim = (char) st.ttype;
}
if (delim == ';') {
throw new IllegalArgumentException(
"illegal delimiter ';'");
}
} else { // rewind
st = createTokenizer(s);
}
st.nextToken();
}
st.quoteChar(delim);
do {
String type, name = null, actions = null;
if (st.ttype != StreamTokenizer.TT_WORD) {
throw new IllegalArgumentException(
"expected permission type");
}
type = st.sval;
// REMIND: allow unquoted name/actions?
st.nextToken();
if (st.ttype == StreamTokenizer.TT_EOF || st.ttype == ';') {
l.add(new PermissionInfo(type, null, null));
continue;
} else if (st.ttype == delim) {
name = st.sval;
} else {
throw new IllegalArgumentException(
"expected permission name or ';'");
}
st.nextToken();
if (st.ttype == StreamTokenizer.TT_EOF || st.ttype == ';') {
l.add(new PermissionInfo(type, name, null));
continue;
} else if (st.ttype != ',') {
throw new IllegalArgumentException("expected ',' or ';'");
}
if (st.nextToken() != delim) {
throw new IllegalArgumentException(
"expected permission actions");
}
actions = st.sval;
st.nextToken();
if (st.ttype == StreamTokenizer.TT_EOF || st.ttype == ';') {
l.add(new PermissionInfo(type, name, actions));
continue;
} else {
throw new IllegalArgumentException("expected ';'");
}
} while (st.nextToken() != StreamTokenizer.TT_EOF);
return (PermissionInfo[]) l.toArray(new PermissionInfo[l.size()]);
} catch (IOException ex) {
throw (Error) new InternalError().initCause(ex);
}
}
/**
* Returns tokenizer for parsing given string. The tokenizer is configured
* similarly to that used by sun.security.provider.PolicyParser, except
* that comments are disabled and no quote character is set (yet).
*/
private static StreamTokenizer createTokenizer(String s) {
StreamTokenizer st = new StreamTokenizer(new StringReader(s));
st.resetSyntax();
st.wordChars('a', 'z');
st.wordChars('A', 'Z');
st.wordChars('.', '.');
st.wordChars('0', '9');
st.wordChars('_', '_');
st.wordChars('$', '$');
st.wordChars(128 + 32, 255);
st.whitespaceChars(0, ' ');
st.lowerCaseMode(false);
st.ordinaryChar('/');
st.slashSlashComments(false);
st.slashStarComments(false);
return st;
}
/**
* Constructs GrantPermission name/target string appropriate for given list
* of permissions.
*/
private static String constructName(Permission[] pa) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < pa.length; i++) {
Permission p = pa[i];
if (p instanceof UnresolvedPermission) {
sb.append(p).append("; ");
} else {
Class cl = p.getClass();
int nargs = maxConsArgs(cl);
String t = cl.getName(), n = p.getName(), a = p.getActions();
if (nargs == 2 && a != null) {
// REMIND: handle null name?
sb.append(t + " " + quote(n) + ", " + quote(a) + "; ");
} else if (nargs >= 1 && n != null) {
sb.append(t + " " + quote(n) + "; ");
} else {
sb.append(t + "; ");
}
}
}
return sb.toString().trim();
}
/**
* Returns the maximum number of String parameters (up to 2) accepted by a
* constructor of the given class. Returns -1 if no matching constructor
* (including no-arg constructor) is defined by given class.
*/
private static int maxConsArgs(Class cl) {
try {
cl.getConstructor(PARAMS2);
return 2;
} catch (Exception ex) {
}
try {
cl.getConstructor(PARAMS1);
return 1;
} catch (Exception ex) {
}
try {
cl.getConstructor(PARAMS0);
return 0;
} catch (Exception ex) {
}
return -1;
}
/**
* Returns quoted string literal that, if parsed by
* java.io.StreamTokenizer, would yield the given string. This method is
* essentially a copy of com.sun.jini.config.ConfigUtil.stringLiteral; the
* two methods are kept separate since ConfigUtil.stringLiteral could
* conceivably escape unicode characters, while such escaping would be
* incorrect for GrantPermission.
*/
private static String quote(String s) {
StringBuffer sb = new StringBuffer(s.length() + 2);
sb.append('"');
char[] ca = s.toCharArray();
for (int i = 0; i < ca.length; i++) {
char c = ca[i];
if (c == '\\' || c == '"') {
sb.append("\\").append(c);
} else if (c == '\n') {
sb.append("\\n");
} else if (c == '\r') {
sb.append("\\r");
} else if (c == '\t') {
sb.append("\\t");
} else if (c == '\f') {
sb.append("\\f");
} else if (c == '\b') {
sb.append("\\b");
} else if (c < 0x20) {
sb.append("\\").append(Integer.toOctalString(c));
} else {
sb.append(c);
}
}
return sb.append('"').toString();
}
/**
* Returns an array containing all non-GrantPermission permissions in the
* given permission array, including those contained in nested
* GrantPermissions in the array.
*/
private static Permission[] flatten(Permission[] pa) {
List l = new ArrayList(pa.length);
for (int i = 0; i < pa.length; i++) {
Permission p = pa[i];
if (p instanceof GrantPermission) {
l.addAll(Arrays.asList(((GrantPermission) p).grants));
} else {
l.add(p);
}
}
return (Permission[]) l.toArray(new Permission[l.size()]);
}
/**
* Parsed information about a permission.
*/
private static class PermissionInfo {
final String type;
final String name;
final String actions;
PermissionInfo(String type, String name, String actions) {
this.type = type;
this.name = name;
this.actions = actions;
}
}
/**
* Class for checking implication of contained permissions.
*/
private static class Implier {
private final PermissionCollection perms = new Permissions();
private final ArrayList unresolved = new ArrayList();
void add(GrantPermission gp) {
for (int i = 0; i < gp.grants.length; i++) {
Permission p = gp.grants[i];
if (!impliesContained(p)) {
perms.add(p);
if (p instanceof UnresolvedPermission) {
unresolved.add(p);
}
}
}
}
boolean implies(Permission p) {
if (!(p instanceof GrantPermission)) {
return false;
}
Permission[] pa = ((GrantPermission) p).grants;
for (int i = 0; i < pa.length; i++) {
if (!impliesContained(pa[i])) {
return false;
}
}
return true;
}
private boolean impliesContained(Permission p) {
if (p instanceof UnresolvedPermission) {
for (Iterator i = unresolved.iterator(); i.hasNext();) {
if (implies((UnresolvedPermission) i.next(),
(UnresolvedPermission) p))
{
return true;
}
}
return false;
} else {
return perms.implies(p);
}
}
private static boolean implies(UnresolvedPermission p1,
UnresolvedPermission p2)
{
if (p1 == p2) {
return true;
}
// REMIND: use UnresolvedPermission.equals() once 4513737 fixed
try {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oout = new ObjectOutputStream(bout);
bout.reset();
oout.writeObject(p1);
oout.flush();
byte[] b1 = bout.toByteArray();
oout.reset();
bout.reset();
oout.writeObject(p2);
oout.flush();
byte[] b2 = bout.toByteArray();
return Arrays.equals(b1, b2);
} catch (IOException ex) {
throw (Error) new InternalError().initCause(ex);
}
}
}
/**
* PermissionCollection variant returned by newPermissionCollection().
*
* @serial include
*/
static class GrantPermissionCollection extends PermissionCollection {
private static final long serialVersionUID = 8227621799817733985L;
/**
* @serialField perms List The permissions.
*/
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("perms", List.class, true)
};
private List perms = new ArrayList();
private Implier implier = new Implier();
public synchronized void add(Permission p) {
if (!(p instanceof GrantPermission)) {
throw new IllegalArgumentException("invalid permission: " + p);
}
if (isReadOnly()) {
throw new SecurityException(
"can't add to read-only PermissionCollection");
}
perms.add(p);
implier.add((GrantPermission) p);
}
public synchronized Enumeration elements() {
return Collections.enumeration(perms);
}
public synchronized boolean implies(Permission p) {
return implier.implies(p);
}
public synchronized void setReadOnly() {
super.setReadOnly();
}
public synchronized boolean isReadOnly() {
return super.isReadOnly();
}
/**
* Writes the permissions list.
*/
private synchronized void writeObject(ObjectOutputStream s)
throws IOException
{
s.defaultWriteObject();
}
/**
* Verifies the permissions list.
*
* @throws InvalidObjectException if the list is
* <code>null</code> or any element is not an instance of
* <code>GrantPermission</code>
*/
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException
{
s.defaultReadObject();
if (perms == null) {
throw new InvalidObjectException("list cannot be null");
}
if (!perms.getClass().equals(ArrayList.class)) {
perms = new ArrayList(perms);
}
if (perms.contains(null)) {
throw new InvalidObjectException(
"element must be a GrantPermission");
}
GrantPermission[] pa;
try {
pa = (GrantPermission[])
perms.toArray(new GrantPermission[perms.size()]);
} catch (ArrayStoreException e) {
throw new InvalidObjectException(
"element must be a GrantPermission");
}
implier = new Implier();
for (int i = 0; i < pa.length; i++) {
implier.add(pa[i]);
}
}
}
}