BaseGrantQuery.java

/*
Copyright (c) 2008 Health Market Science, Inc.

Licensed 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 com.healthmarketscience.sqlbuilder;

import java.io.IOException;

import com.healthmarketscience.common.util.AppendableExt;
import com.healthmarketscience.sqlbuilder.dbspec.Column;
import com.healthmarketscience.sqlbuilder.dbspec.Table;

/**
 * Base of a query which generates a query for manipulating privileges.  Keeps
 * track of the privileges, grantees, and target object.
 *
 * @author James Ahlborn
 */
public abstract class BaseGrantQuery<ThisType extends BaseGrantQuery<ThisType>>
  extends Query<ThisType>
{

  /** grantee object which represents PUBLIC access */
  public static final SqlObject PUBLIC_GRANTEE = new CustomSql("PUBLIC");

  private static final Privilege PRIVILEGE_ALL =
    new Privilege(Privilege.Type.ALL);
  private static final Privilege PRIVILEGE_SELECT =
    new Privilege(Privilege.Type.SELECT);
  private static final Privilege PRIVILEGE_DELETE =
    new Privilege(Privilege.Type.DELETE);
  private static final Privilege PRIVILEGE_USAGE =
    new Privilege(Privilege.Type.USAGE);
  
  protected SqlObjectList<SqlObject> _grantees = SqlObjectList.create();
  protected SqlObjectList<SqlObject> _privileges = SqlObjectList.create();
  protected SqlObject _targetObj;

  
  protected BaseGrantQuery() {
  }

  /**
   * Adds the given custom grantees to the query
   * <p>
   * {@code Object} -&gt; {@code SqlObject} conversions handled by
   * {@link Converter#CUSTOM_TO_OBJ}.
   */
  public ThisType addCustomGrantees(Object... grantees) {
    _grantees.addObjects(Converter.CUSTOM_TO_OBJ, grantees);
    return getThisType();
  }    
    
  /** Adds the given grantees to the query */
  public ThisType addGrantees(String... grantees) {
    return addCustomGrantees((Object[])grantees);
  }

  /**
   * Adds the given privileges to the query.  Generally this should be an
   * instance of a {@link Privilege} object, created by one of the static
   * <code>privilege*</code> methods.
   * <p>
   * {@code Object} -&gt; {@code SqlObject} conversions handled by
   * {@link Converter#CUSTOM_TO_OBJ}.
   */
  public ThisType addCustomPrivileges(Object... privileges) {
    _privileges.addObjects(Converter.CUSTOM_TO_OBJ, privileges);
    return getThisType();
  }

  /** Adds the given privileges to the query.  Generally this should be an
      instance of a {@link Privilege} object, created by one of the static
      <code>privilege*</code> methods. */
  public ThisType addPrivileges(Privilege... privileges) {
    return addCustomPrivileges((Object[])privileges);
  }

  /**
   * Sets the target for the query.  Generally this should be an
   * instance of a {@link TargetObject} object, created by one of the static
   * <code>target*</code> methods.
   * <p>
   * {@code Object} -&gt; {@code SqlObject} conversions handled by
   * {@link Converter#toCustomSqlObject(Object)}.
   */
  public ThisType setCustomTarget(Object target) {
    _targetObj = Converter.toCustomSqlObject(target);
    return getThisType();
  }
  
  /** Sets the target for the query.  Generally this should be an
      instance of a {@link TargetObject} object, created by one of the static
      <code>target*</code> methods. */
  public ThisType setTarget(TargetObject target) {
    return setCustomTarget(target);
  }

  /** @return a Privilege with the type of ALL */
  public static Privilege privilegeAll() {
    return PRIVILEGE_ALL;
  }

  /** @return a Privilege with the type of SELECT */
  public static Privilege privilegeSelect() {
    return PRIVILEGE_SELECT;
  }

  /** @return a Privilege with the type of DELETE */
  public static Privilege privilegeDelete() {
    return PRIVILEGE_DELETE;
  }

  /** @return a Privilege with the type of INSERT and the given (optional)
      columns */
  public static Privilege privilegeInsert(Column... columns) {
    return new Privilege(Privilege.Type.INSERT, columns);
  }

  /** @return a Privilege with the type of UPDATE and the given (optional)
      columns */
  public static Privilege privilegeUpdate(Column... columns) {
    return new Privilege(Privilege.Type.UPDATE, columns);
  }
  
  /** @return a Privilege with the type of REFERENCES and the given (optional)
      columns */
  public static Privilege privilegeReferences(Column... columns) {
    return new Privilege(Privilege.Type.REFERENCES, columns);
  }

  /** @return a Privilege with the type of USAGE */
  public static Privilege privilegeUsage() {
    return PRIVILEGE_USAGE;
  }

  /** @return a TargetObject with the type of TABLE and the given table */
  public static TargetObject targetTable(Table table) {
    return new TargetObject(TargetObject.Type.TABLE, new TableObject(table));
  }
  
  @Override
  protected void collectSchemaObjects(ValidationContext vContext) {
    super.collectSchemaObjects(vContext);
    _grantees.collectSchemaObjects(vContext);
    _privileges.collectSchemaObjects(vContext);
    _targetObj.collectSchemaObjects(vContext);
  }

  @Override
  public void validate(ValidationContext vContext)
    throws ValidationException
  {
    // validate super class
    super.validate(vContext);

    // must have privileges
    if(_privileges.isEmpty()) {
      throw new ValidationException("Must specify privileges");
    }
    // must have grantees
    if(_grantees.isEmpty()) {
      throw new ValidationException("Must specify grantees");
    }
    // if no _targetObj, NPE will be thrown already

    // cannot have additional privileges with ALL
    boolean hasAll = false;
    for(SqlObject privilege : _privileges) {
      if((privilege instanceof Privilege) &&
         (((Privilege)privilege)._type == Privilege.Type.ALL)) {
        hasAll = true;
        break;
      }
    }
    if(hasAll && (_privileges.size() > 1)) {
      throw new ValidationException("May not have other privileges with ALL");
    }
  }

  /**
   * Encapsulation of a database privilege.
   */
  public static class Privilege extends SqlObject
  {
    /** Enumeration representing the various database privilege types */
    public enum Type {
      ALL("ALL PRIVILEGES", false),
      SELECT("SELECT", false),
      DELETE("DELETE", false),
      INSERT("INSERT", true),
      UPDATE("UPDATE", true),
      REFERENCES("REFERENCES", true),
      USAGE("USAGE", false);

      private final String _typeStr;
      private final boolean _maySpecifyColumns;

      private Type(String typeStr, boolean maySpecifyColumns) {
        _typeStr = typeStr;
        _maySpecifyColumns = maySpecifyColumns;
      }

      public boolean maySpecifyColumns() {
        return _maySpecifyColumns;
      }
      
      @Override
      public String toString() { return _typeStr; }      
    }

    private Type _type;
    private SqlObjectList<SqlObject> _columns = SqlObjectList.create();

    public Privilege(Type type, Column... columns) {
      _type = type;
      if(_type.maySpecifyColumns()) {
        _columns.addObjects(Converter.COLUMN_TO_OBJ, columns);
      }
    }
    
    /**
     * Adds the given columns to the column list.
     * <p>
     * {@code Object} -&gt; {@code SqlObject} conversions handled by
     * {@link Converter#CUSTOM_COLUMN_TO_OBJ}.
     */
    public Privilege addCustomColumns(Object... columnStrs) {
      if(_type.maySpecifyColumns()) {
        _columns.addObjects(Converter.CUSTOM_COLUMN_TO_OBJ, columnStrs);
      }
      return this;
    }

    /** Adds the given columns to the column list. */
    public Privilege addColumns(Column... columns) {
      return addCustomColumns((Object[])columns);
    }

    @Override
    protected void collectSchemaObjects(ValidationContext vContext) {
      _columns.collectSchemaObjects(vContext);
    }

    @Override
    public void appendTo(AppendableExt app) throws IOException {
      app.append(_type);
      if(!_columns.isEmpty()) {
        app.append("(").append(_columns).append(")");
      }
    }    
  }

  /**
   * Information about the database object upon which a privilege allows (or
   * disallows) action.
   */
  public static class TargetObject extends SqlObject
  {
    /** Enumeration representing the types of database objects which have
        privileges for interacting with them. */
    public enum Type {
      TABLE("TABLE "),
      DOMAIN("DOMAIN "),
      COLLATION("COLLATION "),
      CHARACTER_SET("CHARACTER SET "),
      TRANSLATION("TRANSLATION ");

      private final String _typeStr;

      private Type(String typeStr) {
        _typeStr = typeStr;
      }

      @Override
      public String toString() { return _typeStr; }      
    }

    private Type _type;
    private SqlObject _name;

    public TargetObject(Type type, SqlObject name) {
      _type = type;
      _name = name;
    }

    @Override
    protected void collectSchemaObjects(ValidationContext vContext) {
      _name.collectSchemaObjects(vContext);
    }

    @Override
    public void appendTo(AppendableExt app) throws IOException {
      app.append(_type).append(_name);
    }    
  }
  
}