SparceStringMatrix.java

/*
 * Created on 2004/02/04
 * Copyright (C) 2004 Koga Laboratory. All rights reserved.
 */
package org.mklab.tool.control.system.graph;

import java.io.PrintWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

import org.mklab.nfc.matrix.AbstractGrid;
import org.mklab.nfc.matrix.IntMatrix;


/**
 * 文字列を成分とする疎な行列を表現するクラスです。
 * 
 * @author Yusuke Tsutsui
 * @version $Revision: 1.10 $, 2006/11/03
 */
public class SparceStringMatrix extends AbstractGrid<SparceStringMatrix> {

  /** シリアル番号 */
  private static final long serialVersionUID = 152527032306625817L;

  /** 行列の成分 */
  private Map<Position, String> elementMap = new HashMap<>();

  /**
   * 0x0の文字列行列を生成します。
   */
  public SparceStringMatrix() {
    this(0, 0);
  }

  //  /**
  //   * コンストラクター
  //   * 
  //   * @param matrix
  //   *        文字列で構成された伝達関数行列
  //   */
  //  public SparceStringMatrix(final SparceStringMatrix matrix) {
  //    this.rowSize = matrix.rowSize;
  //    this.columnSize = matrix.columnSize; 
  //
  //    for (int row = 1; row <= matrix.rowSize; row++) {
  //      for (int column = 1; column <= matrix.columnSize; column++) {
  //        setElement(row, column, matrix.getElement(row, column));
  //      }
  //    }
  //  }

  /**
   * <code>elements</code>で与えられた成分をもつ行ベクトルを生成します。
   * 
   * @param elements {@link String}の配列
   */
  public SparceStringMatrix(final String[] elements) {
    this(new String[][] {elements});
  }

  /**
   * コンストラクター
   * 
   * @param rowSize 行の数
   * @param columnSize 列の数
   */
  public SparceStringMatrix(final int rowSize, final int columnSize) {
    this(new String[rowSize][columnSize]);
  }

  /**
   * <code>elements</code>で与えられた成分をもつ文字列行列を生成します。
   * 
   * @param elements {@link String}の2次元配列
   */
  public SparceStringMatrix(final String[][] elements) {
    super(elements.length, elements.length == 0 ? 0 : elements[0].length);

    //this.rowSize = elements.length;
    //this.columnSize = getRowSize() == 0 ? 0 : elements[0].length;

    for (int row = 1; row <= getRowSize(); row++) {
      String[] rowMatrix = elements[row - 1];
      for (int column = 1; column <= getColumnSize(); column++) {
        setElement(row, column, rowMatrix[column - 1]);
      }
    }
  }

  /**
   * <code>row</code>行<code>column</code>列の成分を返します。
   * 
   * @param row 行番号
   * @param column 列番号
   * @return (row,column)成分
   */
  public String getElement(final int row, final int column) {
    Position pos = new Position(row, column);

    if (this.elementMap.containsKey(pos)) {
      return this.elementMap.get(pos);
    }
    return ""; //$NON-NLS-1$
  }

  /**
   * 指定した位置に値が入っているか判定します。
   * 
   * @param row 行番号
   * @param column 列番号
   * @return 指定した位置に値が入っていればtrue、入っていなければfalse
   */
  public boolean hasElement(final int row, final int column) {
    if (this.elementMap.containsKey(new Position(row, column))) {
      return true;
    }
    return false;
  }

  /**
   * 指定した位置に値を設定します。
   * 
   * @param row 行番号
   * @param column 列番号
   * @param value (row,column)に設定する {@link String}
   */
  public void setElement(final int row, final int column, final String value) {
    Position pos = new Position(row, column);
    if (value == null || value.equals("")) { //$NON-NLS-1$
      this.elementMap.remove(pos);
    } else {
      this.elementMap.put(pos, value);
    }
  }

  /**
   * @see org.mklab.nfc.matrix.Grid#printElements(java.io.Writer)
   */
  @Override
  public void printElements(final Writer output) {
    int maxColumnSize = Integer.MAX_VALUE;
    printElements(output, maxColumnSize);
  }

  /**
   * @see org.mklab.nfc.matrix.Grid#printElements(java.io.Writer, int)
   */
  @Override
  public void printElements(final Writer output,  final int maxColumnSize) {
    final PrintWriter pw = new PrintWriter(output);

    int elementWidth = 0;
    for (int row = 1; row <= getRowSize(); row++) {
      for (int column = 1; column <= getColumnSize(); column++) {
        elementWidth = Math.max(elementWidth, getElement(row, column).length());
      }
    }
    elementWidth++;

    for (int row = 1; row <= getRowSize(); row++) {
      for (int column = 1; column <= getColumnSize(); column++) {
        String element = getElement(row, column);
        int cur;
        if (element.equals("")) { //$NON-NLS-1$
          pw.print("\"\""); //$NON-NLS-1$
          cur = 2;
        } else {
          pw.print(element);
          cur = element.length();
        }

        for (int k = cur; k < elementWidth; k++) {
          pw.print(" "); //$NON-NLS-1$
        }
      }
      pw.println();
    }
    pw.println();

    pw.flush();
  }

  /**
   * 指定されたノードを削除した文字列行列を返します。
   * 
   * @param node 削除対象のノード
   * @return 削除対象ノードを削除した文字列行列
   */
  public SparceStringMatrix removeNode(final int node) {
    return removeRowAndColumn(node);
  }

  /**
   * 指定された行と列を削除した文字列行列を返します。
   * 
   * @param n 削除対象の行番号、列番号
   * @return 指定された行と列を削除した文字列行列
   */
  public SparceStringMatrix removeRowAndColumn(final int n) {
    SparceStringMatrix result = new SparceStringMatrix(getRowSize() - 1, getColumnSize() - 1);

    for (int row = 1; row <= getRowSize() - 1; row++) {
      for (int column = 1; column <= getColumnSize() - 1; column++) {
        int r = row;
        int c = column;
        if (n <= row) {
          r = row + 1;
        }
        if (n <= column) {
          c = column + 1;
        }
        result.setElement(row, column, getElement(r, c));
      }
    }

    return result;
  }

  /**
   * @see org.mklab.nfc.matrix.Grid#exchangeRow(int, int)
   */
  @Override
  public void exchangeRow(final int row1, final int row2) {
    for (int column = 1; column <= getColumnSize(); column++) {
      String element1 = getElement(row1, column);
      String element2 = getElement(row2, column);
      setElement(row2, column, element1);
      setElement(row1, column, element2);
    }
  }

  /**
   * @see org.mklab.nfc.matrix.Grid#exchangeColumn(int, int)
   */
  @Override
  public void exchangeColumn(final int column1, final int column2) {
    for (int row = 1; row <= getRowSize(); row++) {
      String element1 = getElement(row, column1);
      String element2 = getElement(row, column2);
      setElement(row, column2, element1);
      setElement(row, column1, element2);
    }
  }

  /**
   * 行n1と行n2、列n1と列n2を同時に入れ替えた行列を返します。
   * 
   * @param n1 行番号1、列番号1
   * @param n2 行番号2、列番号2
   * @return 行1と行2、列1と列2を入れ替えた文字行列
   */
  public SparceStringMatrix exchangeRowAndColumn(final int n1, final int n2) {
    SparceStringMatrix result = new SparceStringMatrix(getRowSize(), getColumnSize());

    for (int row = 1; row <= getRowSize(); row++) {
      for (int column = 1; column <= getColumnSize(); column++) {
        int r = row;
        int c = column;

        if (row == n1) {
          r = n2;
        } else if (row == n2) {
          r = n1;
        }

        if (column == n1) {
          c = n2;
        } else if (column == n2) {
          c = n1;
        }
        result.setElement(row, column, getElement(r, c));
      }
    }
    return result;
  }

  /**
   * 指定した行に存在する空文字列でない成分の数を返します。
   * 
   * @param row 行番号
   * @return 指定した行に存在する空文字列でない成分の数
   */
  public int getRowElementCount(final int row) {
    int count = 0;
    for (int column = 1; column <= getColumnSize(); column++) {
      if (this.hasElement(row, column)) {
        count++;
      }
    }
    return count;
  }

  /**
   * 指定した列に存在する空文字列でない成分の数を返します。
   * 
   * @param column 列番号
   * @return 指定した列に存在する空文字列でない成分の数
   */
  public int getColumnElementCount(final int column) {
    int count = 0;
    for (int row = 1; row <= getRowSize(); row++) {
      if (this.hasElement(row, column)) {
        count++;
      }
    }
    return count;
  }

  /**
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public boolean equals(Object opponent) {
    if (this == opponent) {
      return true;
    }
    if (opponent == null) {
      return false;
    }
    if (opponent.getClass() != getClass()) {
      return false;
    }
    SparceStringMatrix castedObj = (SparceStringMatrix)opponent;
    return ((this.elementMap == null ? castedObj.elementMap == null : this.elementMap.equals(castedObj.elementMap)) && (getRowSize() == castedObj.getRowSize()) && (getColumnSize() == castedObj
        .getColumnSize()));
  }

  /**
   * @see java.lang.Object#hashCode()
   */
  @Override
  public int hashCode() {
    int hashCode = 1;
    hashCode = 31 * hashCode + (this.elementMap == null ? 0 : this.elementMap.hashCode());
    hashCode = 31 * hashCode + getRowSize();
    hashCode = 31 * hashCode + getColumnSize();
    return hashCode;
  }

  /**
   * @see org.mklab.nfc.matrix.AbstractGrid#clone()
   */
  @Override
  public SparceStringMatrix clone() {
    try {
      SparceStringMatrix ans = (SparceStringMatrix)super.clone();
      ans.elementMap = new HashMap<>();
      ans.elementMap.putAll(this.elementMap);
      return ans;
    } catch (CloneNotSupportedException e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * @see org.mklab.nfc.matrix.Grid#isZero()
   */
  @Override
  public boolean isZero() {
    return this.elementMap.size() == 0;
  }

  /**
   * 行列の成分の位置を表現するクラスです。
   * 
   * @author yusuke
   * @version $Revision: 1.10 $, 2005/11/07
   */
  private static class Position {

    /** 行番号 */
    private int row;
    /** 列番号 */
    private int column;

    /**
     * 新しく生成された<code>Position</code>オブジェクトを初期化します。
     * 
     * @param row 行番号
     * @param column 列番号
     */
    public Position(final int row, final int column) {
      this.row = row;
      this.column = column;
    }

    /**
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object opponent) {
      if (this == opponent) {
        return true;
      }
      if (opponent == null) {
        return false;
      }
      if (opponent.getClass() != getClass()) {
        return false;
      }
      Position castedObj = (Position)opponent;
      return ((this.row == castedObj.row) && (this.column == castedObj.column));
    }

    /**
     * Override hashCode.
     * 
     * @return the Objects hashcode.
     */
    @Override
    public int hashCode() {
      int hashCode = 1;
      hashCode = 31 * hashCode + this.row;
      hashCode = 31 * hashCode + this.column;
      return hashCode;
    }
  }

  /**
   * @see org.mklab.nfc.matrix.Grid#removeColumnVectors(int, int)
   */
  @Override
  public void removeColumnVectors( int columnMin,  int columnMax) {
    throw new UnsupportedOperationException();
  }

  /**
   * @see org.mklab.nfc.matrix.Grid#removeColumnVectors(org.mklab.nfc.matrix.IntMatrix)
   */
  @Override
  public void removeColumnVectors( IntMatrix columnIndex) {
    throw new UnsupportedOperationException();
  }

  /**
   * @see org.mklab.nfc.matrix.Grid#removeRowVectors(int, int)
   */
  @Override
  public void removeRowVectors( int rowMin,  int rowMax) {
    throw new UnsupportedOperationException();
  }

  /**
   * @see org.mklab.nfc.matrix.Grid#removeRowVectors(org.mklab.nfc.matrix.IntMatrix)
   */
  @Override
  public void removeRowVectors( IntMatrix rowIndex) {
    throw new UnsupportedOperationException();
  }
}