Package org.kiji.schema.tools

Source Code of org.kiji.schema.tools.NotesTool

/**
* (c) Copyright 2013 WibiData, Inc.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* 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 org.kiji.schema.tools;

import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import com.google.gson.Gson;

import org.kiji.annotations.ApiAudience;
import org.kiji.annotations.ApiStability;
import org.kiji.common.flags.Flag;
import org.kiji.schema.InternalKijiError;
import org.kiji.schema.Kiji;
import org.kiji.schema.KijiColumnName;
import org.kiji.schema.KijiTable;
import org.kiji.schema.KijiTableAnnotator;
import org.kiji.schema.KijiURI;
import org.kiji.schema.KijiURIException;

/**
* CLI for setting, removing, and getting column and table annotations.
*
* <h3>Example Usage:</h3>
* <p>
*   <ul>
*     <li>
*       Set sample_key -> sample_value for table: "table":
*       <pre>
*         kiji notes --target=kiji://.env/default/table \
*             --do=set \
*             --key=sample_key \
*             --value=sample_value
*       </pre>
*     </li>
*     <li>
*       Set sample_key -> sample_value and sample_key_2 -> sample_value_2 for qualified column
*       "family:qualfiier" in table "table":
*       <pre>
*         kiji notes --target=kiji://.env/default/table/family:qualifier \
*             --do=set \
*             --map={"sample_key":"sample_value","sample_key_2":"sample_value_2"}
*       </pre>
*     </li>
*     <li>
*       Remove all annotations from "family:qualifier" in table "table":
*       <pre>
*         kiji notes --target=kiji://.env/default/table/family:qualifier \
*             --do=remove
*       </pre>
*     </li>
*     <li>
*       Get all annotations starting with the given key prefix on columns in the family "family" in
*       table "table":
*       <pre>
*         kiji notes --target=kiji://.env/default/table \
*             --do=get
*             --in-family=family
*             --key=sample_
*             --prefix
*       </pre>
*     </li>
*     <li>
*       Get all annotations matching the given regex (letters only) on the family "family" (not
*       columns in "family") in table "table":
*       <pre>
*         kiji notes --target=kiji://.env/default/table/family \
*             --do=get
*             --key=^[a-zA-Z]*&
*             --regex
*       </pre>
*     </li>
*   </ul>
* </p>
*/
@ApiAudience.Public
@ApiStability.Experimental
public final class NotesTool extends BaseTool {

  @Flag(name="target", usage="Specify the KijiURI of the target of your operation. Must include at "
      + "least a table and may include exactly one column.")
  private String mTargetFlag = "";

  @Flag(name="do", usage="Pick exactly one of:\n"
      + "  'set' to set one or more key value pair for a column or table.\n"
      + "    (requires --key and --value OR --map)\n"
      + "  'remove' to remove key value pairs from a column or table.\n"
      + "    (use --prefix, --partial, or --regex to remove keys which match the given criteria.\n"
      + "  'get' to get key value pairs from a column or table.\n"
      + "    (use --prefix, --partial, or --regex to get keys which match the given criteria.\n")
  private String mDoFlag = "";

  @Flag(name="key", usage="Specify an annotation key. Keys must match the pattern "
      + KijiTableAnnotator.ALLOWED_ANNOTATION_KEY_PATTERN + "\n  (use --prefix, --partial, or "
      + "--regex to treat this as a matcher instead of an exact key.)\n  (if unset while "
      + "--do=remove or --do=get, all annotations will be removed or retrieved)")
  private String mKeyFlag = "";

  @Flag(name="value", usage="Specify an annotation value. Values may be any string.")
  private String mValueFlag = "";

  @Flag(name="map", usage="Specify a json map of annotation key-values. Keys must match the "
      + "pattern " + KijiTableAnnotator.ALLOWED_ANNOTATION_KEY_PATTERN + ", values may be any "
      + "string.")
  private String mMapFlag = "";

  @Flag(name="prefix", usage="Specify to treat the --key flag as a key prefix.\n"
      + "Usable only with --do=get")
  private Boolean mPrefixFlag = false;

  @Flag(name="partial", usage="Specify to treat the --key flag as a partial key.\n"
      + "Usable only with --do=get")
  private Boolean mPartialFlag = false;

  @Flag(name="regex", usage="Specify to treat the --key flag as a regex pattern.\n"
      + "Usable only with --do=get")
  private Boolean mRegexFlag = false;

  @Flag(name="in-family", usage="Specify to get or remove keys from the given family.\n"
      + "  (requires no columns specified in --target)")
  private String mFamilyFlag = "";

  @Flag(name="all-columns", usage="Specify to get or remove keys from all columns in the target "
      + "table, rather than the table itself.\n"
      + "  (requires no columns specified in --target and --do=remove or --do=get)")
  private Boolean mAllColumnsFlag = false;

  private DoMode mDoMode = null;
  private KijiURI mURI = null;
  private KijiTableAnnotator mAnnotator = null;

  /** {@inheritDoc} */
  @Override
  public String getName() {
    return "notes";
  }

  /** {@inheritDoc} */
  @Override
  public String getDescription() {
    return "Set, remove, and get column and table annotations.";
  }

  /** {@inheritDoc} */
  @Override
  public String getCategory() {
    return "Metadata";
  }

  /** Possible operations of this tool. */
  private static enum DoMode {
    SET, REMOVE, GET
  }

  private static final Gson GSON = new Gson();

  /**
   * Deserialize a String-String map from JSON.
   *
   * @param json the map to deserialize.
   * @return the String-String map encoded in the json.
   */
  private static Map<String, String> mapFromJson(
      final String json
  ) {
    return GSON.fromJson(json, Map.class);
  }

  /**
   * Check if more than one of the specified booleans is true.
   *
   * @param bools the booleans to check.
   * @return whether mroe than one of the specified booleans is true.
   */
  private static boolean moreThanOne(boolean... bools) {
    int totalTrue = 0;
    for (boolean b : bools) {
      if (b) {
        totalTrue++;
      }
    }
    return totalTrue > 1;
  }

  /**
   * Print an annotation on the table specified in mURI.
   *
   * @param key the annotation key.
   * @param value the annotation value.
   */
  private void printTableAnnotation(
      final String key,
      final String value
  ) {
    getPrintStream().printf("table: '%s'%n"
        + "  '%s': '%s'%n", mURI.getTable(), key, value);
  }

  /**
   * Print several annotation key values on the table specified in mURI.
   *
   * @param kvs a mapping of keys and values.
   */
  private void printTableAnnotations(
      final Map<String, String> kvs
  ) {
    getPrintStream().printf("table: '%s'%n", mURI.getTable());
    for (Map.Entry<String, String> kv : kvs.entrySet()) {
      getPrintStream().printf("  '%s': '%s'%n", kv.getKey(), kv.getValue());
    }
  }

  /**
   * Print a column and an annotation key value.
   *
   * @param column the annotated column.
   * @param key the annotation key.
   * @param value the annotation value.
   */
  private void printColumnAnnotation(
      final KijiColumnName column,
      final String key,
      final String value
  ) {
    getPrintStream().printf("column: '%s'%n"
        + "  '%s': '%s'%n", column, key, value);
  }

  /**
   * Print a column and several annotation key values.
   *
   * @param column the annotated column.
   * @param kvs a mapping of the keys and values.
   */
  private void printColumnAnnotations(
      final KijiColumnName column,
      final Map<String, String> kvs
  ) {
    getPrintStream().printf("column: '%s'%n", column);
    for (Map.Entry<String, String> kv: kvs.entrySet()) {
      getPrintStream().printf("  '%s': '%s'%n", kv.getKey(), kv.getValue());
    }
  }

  /**
   * Print several column annotations with the same key.
   *
   * @param key the key for all annotations.
   * @param cvs a map from columns to the value at the given annotation key.
   */
  private void printColumnAnnotations(
      final String key,
      final Map<KijiColumnName, String> cvs
  ) {
    for (Map.Entry<KijiColumnName, String> cvEntry : cvs.entrySet()) {
      printColumnAnnotation(cvEntry.getKey(), key, cvEntry.getValue());
    }
  }

  /**
   * Print several columns and annotations associated with those columns.
   *
   * @param ckvs a mapping from column to key to value.
   */
  private void printColumnAnnotations(
      final Map<KijiColumnName, Map<String, String>> ckvs
  ) {
    for (Map.Entry<KijiColumnName, Map<String, String>> columnEntry : ckvs.entrySet()) {
      printColumnAnnotations(columnEntry.getKey(), columnEntry.getValue());
    }
  }

  /**
   * Print a removed table annotation.
   *
   * @param removedKey the removed annotation key.
   */
  private void printRemovedTableAnnotation(
      final String removedKey
  ) {
    getPrintStream().printf("table: '%s'%n  '%s'%n", mURI.getTable(), removedKey);
  }

  /**
   * Print several removed table annotations.
   *
   * @param removedKeys the set of removed annotation keys.
   */
  private void printRemovedTableAnnotations(
      final Set<String> removedKeys
  ) {
    getPrintStream().printf("table: '%s'%n", mURI.getTable());
    for (String key : removedKeys) {
      getPrintStream().printf("  '%s'%n", key);
    }
  }

  /**
   * Print a removed column annotation.
   *
   * @param column column from which the annotation was removed.
   * @param removedKey removed annotation key.
   */
  private void printRemovedColumnAnnotation(
      final KijiColumnName column,
      final String removedKey
  ) {
    getPrintStream().printf("column: '%s'%n  '%s'", column, removedKey);
  }

  /**
   * Print several removed annotations from a single column.
   *
   * @param column column from which annotations were removed.
   * @param removedKeys removed annotation keys.
   */
  private void printRemovedColumnAnnotations(
      final KijiColumnName column,
      final Set<String> removedKeys
  ) {
    getPrintStream().printf("column: '%s'%n", column);
    for (String key: removedKeys) {
      getPrintStream().printf("  '%s'%n", key);
    }
  }

  /**
   * Print several removed annotations from several columns.
   *
   * @param removedKeys map from column to annotation keys removed from that column.
   */
  private void printRemovedColumnAnnotations(
      final Map<KijiColumnName, Set<String>> removedKeys
  ) {
    for (Map.Entry<KijiColumnName, Set<String>> columnEntry : removedKeys.entrySet()) {
      printRemovedColumnAnnotations(columnEntry.getKey(), columnEntry.getValue());
    }
  }

  /** {@inheritDoc} */
  @Override
  protected void validateFlags() {
    try {
      mURI = KijiURI.newBuilder(mTargetFlag).build();
    } catch (KijiURIException kurie) {
      exitWithErrorMessage(kurie.getMessage());
    }
    if (null == mURI.getTable()) {
      exitWithFormattedErrorMessage("--target URI must specify at least a table, got: '%s'", mURI);
    }
    if (1 < mURI.getColumns().size()) {
      exitWithErrorMessage("--target URI must include one or zero columns.");
    }
    try {
      mDoMode = DoMode.valueOf(mDoFlag.toUpperCase(Locale.ROOT).replace('-', '_'));
    } catch (IllegalArgumentException iae) {
      exitWithFormattedErrorMessage(
          "Invalid --do command: '%s'. Valid commands are set, remove, get", mDoFlag);
    }
    switch (mDoMode) {
      case SET: {
        if (mPrefixFlag || mPartialFlag || mRegexFlag) {
          exitWithErrorMessage("Cannot specify --prefix, --partial, or --regex while --do=set");
        }
        if (!mFamilyFlag.isEmpty()) {
          exitWithErrorMessage("Cannot specify a family with --in-family while --do=set");
        }
        if (mMapFlag.isEmpty() && (mKeyFlag.isEmpty() || mValueFlag.isEmpty())) {
          exitWithErrorMessage("--do=set requires --key and --value OR --map");
        }
        if (!mMapFlag.isEmpty() && (!mKeyFlag.isEmpty() || !mValueFlag.isEmpty())) {
          exitWithErrorMessage("--map is mutually exclusive with --key and --value");
        }
        break;
      }
      case REMOVE: {
        if (moreThanOne(mPrefixFlag, mPartialFlag, mRegexFlag)) {
          exitWithErrorMessage("Cannot specify more than one of --prefix, --partial, and --regex");
        }
        if (!mMapFlag.isEmpty()) {
          exitWithErrorMessage("Cannot specify --map while --do=remove");
        }
        if (!mValueFlag.isEmpty()) {
          exitWithErrorMessage("Cannot specify --value while --do=remove");
        }
        break;
      }
      case GET: {
        if (moreThanOne(mPrefixFlag, mPartialFlag, mRegexFlag)) {
          exitWithErrorMessage("Cannot specify more than one of --prefix, --partial, and --regex");
        }
        if (!mMapFlag.isEmpty()) {
          exitWithErrorMessage("Cannot specify --map while --do=get");
        }
        if (!mValueFlag.isEmpty()) {
          exitWithErrorMessage("Cannot specify --value while --do=get");
        }
        break;
      }
      default: throw new InternalKijiError(String.format("Unknown DoMode: %s", mDoMode));
    }
    if (!mFamilyFlag.isEmpty() && !mURI.getColumns().isEmpty()) {
      exitWithErrorMessage("--in-family requires that no columns are specified in --target");
    }
  }

  /**
   * Set annotations.
   *
   * @return a tool exit code.
   * @throws IOException in case of an error writing to the meta table.
   */
  private int set() throws IOException {
    final List<KijiColumnName> columns = mURI.getColumns();
    if (columns.isEmpty()) {
      if (mMapFlag.isEmpty()) {
        mAnnotator.setTableAnnotation(mKeyFlag, mValueFlag);
        getPrintStream().println("Annotation added:");
        printTableAnnotation(mKeyFlag, mValueFlag);
      } else {
        final Map<String, String> kvs = mapFromJson(mMapFlag);
        mAnnotator.setTableAnnotations(kvs);
        getPrintStream().println("Annotations added:");
        printTableAnnotations(kvs);
      }
    } else {
      for (KijiColumnName column : columns) {
        if (mMapFlag.isEmpty()) {
          mAnnotator.setColumnAnnotation(column, mKeyFlag, mValueFlag);
          getPrintStream().println("Annotation added:");
          printColumnAnnotation(column, mKeyFlag, mValueFlag);
        } else {
          final Map<String, String> kvs = mapFromJson(mMapFlag);
          mAnnotator.setColumnAnnotations(column, kvs);
          getPrintStream().println("Annotations added:");
          printColumnAnnotations(column, kvs);
        }
      }
    }
    return BaseTool.SUCCESS;
  }

  /**
   * Remove annotations.
   *
   * @return a tool exit code.
   * @throws IOException in case of an error reading or writing to the meta table.
   */
  private int remove() throws IOException {
    final List<KijiColumnName> columns = mURI.getColumns();
    if (columns.isEmpty()) {
      if (mKeyFlag.isEmpty()) {
        if (mAllColumnsFlag) {
          getPrintStream().println("Annotations removed:");
          printRemovedColumnAnnotations(mAnnotator.removeAllColumnAnnotations());
        } else {
          if (!mFamilyFlag.isEmpty()) {
            getPrintStream().println("Annotations removed:");
            printRemovedColumnAnnotations(
                mAnnotator.removeAllColumnAnnotationsInFamily(mFamilyFlag));
          } else {
            getPrintStream().println("Annotations removed:");
            printRemovedTableAnnotations(mAnnotator.removeAllTableAnnotations());
          }
        }
      } else {
        if (mFamilyFlag.isEmpty()) {
          if (mPrefixFlag) {
            getPrintStream().println("Annotations removed:");
            printRemovedTableAnnotations(mAnnotator.removeTableAnnotationsStartingWith(mKeyFlag));
          } else if (mPartialFlag) {
            getPrintStream().println("Annotations removed:");
            printRemovedTableAnnotations(mAnnotator.removeTableAnnotationsContaining(mKeyFlag));
          } else if (mRegexFlag) {
            getPrintStream().println("Annotations removed:");
            printRemovedTableAnnotations(mAnnotator.removeTableAnnotationsMatching(mKeyFlag));
          } else {
            getPrintStream().println("Annotation removed:");
            mAnnotator.removeTableAnnotation(mKeyFlag);
            printRemovedTableAnnotation(mKeyFlag);
          }
        } else {
          if (mPrefixFlag) {
            getPrintStream().println("Annotations removed:");
            printRemovedColumnAnnotations(
                mAnnotator.removeColumnAnnotationsInFamilyStartingWith(mFamilyFlag, mKeyFlag));
          } else if (mPartialFlag) {
            getPrintStream().println("Annotations removed:");
            printRemovedColumnAnnotations(
                mAnnotator.removeColumnAnnotationsInFamilyContaining(mFamilyFlag, mKeyFlag));
          } else if (mRegexFlag) {
            getPrintStream().println("Annotations removed:");
            printRemovedColumnAnnotations(
                mAnnotator.removeColumnAnnotationsInFamilyMatching(mFamilyFlag, mKeyFlag));
          } else {
            final Set<KijiColumnName> removedColumns =
                mAnnotator.removeColumnAnnotationsInFamily(mFamilyFlag, mKeyFlag);
            getPrintStream().println("Annotations removed:");
            getPrintStream().printf("key: '%s'%n", mKeyFlag);
            for (KijiColumnName column : removedColumns) {
              getPrintStream().printf("  '%s'%n", column);
            }
          }
        }
      }
    } else {
      if (mKeyFlag.isEmpty()) {
        for (KijiColumnName column : columns) {
          getPrintStream().println("Annotations removed:");
          printRemovedColumnAnnotations(column, mAnnotator.removeAllColumnAnnotations(column));
        }
      } else {
        for (KijiColumnName column : columns) {
          if (mPrefixFlag) {
            getPrintStream().println("Annotations removed:");
            printRemovedColumnAnnotations(
                column, mAnnotator.removeColumnAnnotationsStartingWith(column, mKeyFlag));
          } else if (mPartialFlag) {
            getPrintStream().println("Annotations removed:");
            printRemovedColumnAnnotations(
                column, mAnnotator.removeColumnAnnotationsContaining(column, mKeyFlag));
          } else if (mRegexFlag) {
            getPrintStream().println("Annotations removed:");
            printRemovedColumnAnnotations(
                column, mAnnotator.removeColumnAnnotationsMatching(column, mKeyFlag));
          } else {
            getPrintStream().println("Annotation removed:");
            mAnnotator.removeColumnAnnotation(column, mKeyFlag);
            printRemovedColumnAnnotation(column, mKeyFlag);
          }
        }
      }
    }
    return BaseTool.SUCCESS;
  }

  /**
   * Get and print annotations.
   *
   * @return a tool exit code.
   * @throws IOException in case of an error reading from the meta table.
   */
  private int get() throws IOException {
    final List<KijiColumnName> columns = mURI.getColumns();
    if (columns.isEmpty()) {
      if (mKeyFlag.isEmpty()) {
        if (mAllColumnsFlag) {
          printColumnAnnotations(mAnnotator.getAllColumnAnnotations());
        } else {
          if (!mFamilyFlag.isEmpty()) {
            printColumnAnnotations(mAnnotator.getAllColumnAnnotationsInFamily(mFamilyFlag));
          } else {
            printTableAnnotations(mAnnotator.getAllTableAnnotations());
          }
        }
      } else {
        if (mFamilyFlag.isEmpty()) {
          if (mPrefixFlag) {
            printTableAnnotations(mAnnotator.getTableAnnotationsStartingWith(mKeyFlag));
          } else if (mPartialFlag) {
            printTableAnnotations(mAnnotator.getTableAnnotationsContaining(mKeyFlag));
          } else if (mRegexFlag) {
            printTableAnnotations(mAnnotator.getTableAnnotationsMatching(mKeyFlag));
          } else  {
            printTableAnnotation(mKeyFlag, mAnnotator.getTableAnnotation(mKeyFlag));
          }
        } else {
          if (mPrefixFlag) {
            printColumnAnnotations(
                mAnnotator.getColumnAnnotationsInFamilyStartingWith(mFamilyFlag, mKeyFlag));
          } else if (mPartialFlag) {
            printColumnAnnotations(
                mAnnotator.getColumnAnnotationsInFamilyContaining(mFamilyFlag, mKeyFlag));
          } else if (mRegexFlag) {
            printColumnAnnotations(
                mAnnotator.getColumnAnnotationsInFamilyMatching(mFamilyFlag, mKeyFlag));
          } else  {
            printColumnAnnotations(
                mKeyFlag, mAnnotator.getColumnAnnotationsInFamily(mFamilyFlag, mKeyFlag));
          }
        }
      }
    } else {
      if (mKeyFlag.isEmpty()) {
        for (KijiColumnName column : columns) {
          printColumnAnnotations(column, mAnnotator.getAllColumnAnnotations(column));
        }
      } else {
        for (KijiColumnName column : columns) {
          if (mPrefixFlag) {
            printColumnAnnotations(
                column, mAnnotator.getColumnAnnotationsStartingWith(column, mKeyFlag));
          } else if (mPartialFlag) {
            printColumnAnnotations(
                column, mAnnotator.getColumnAnnotationsContaining(column, mKeyFlag));
          } else if (mRegexFlag) {
            printColumnAnnotations(
                column, mAnnotator.getColumnAnnotationsMatching(column, mKeyFlag));
          } else {
            printColumnAnnotation(
                column, mKeyFlag, mAnnotator.getColumnAnnotation(column, mKeyFlag));
          }
        }
      }
    }
    return BaseTool.SUCCESS;
  }

  /** {@inheritDoc} */
  @Override
  protected int run(final List<String> nonFlagArgs) throws Exception {
    final Kiji kiji = Kiji.Factory.open(mURI);
    try {
      final KijiTable table = kiji.openTable(mURI.getTable());
      try {
        mAnnotator = table.openTableAnnotator();
        try {
          switch (mDoMode) {
            case SET: return set();
            case REMOVE: return remove();
            case GET: return get();
            default: throw new InternalKijiError("Unknown NotesTool DoMode: " + mDoMode);
          }
        } finally {
          mAnnotator.close();
        }
      } finally {
        table.release();
      }
    } finally {
      kiji.release();
    }
  }
}
TOP

Related Classes of org.kiji.schema.tools.NotesTool

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.