com.threerings.tools.gxlate.spreadsheet.Index.java Source code

Java tutorial

Introduction

Here is the source code for com.threerings.tools.gxlate.spreadsheet.Index.java

Source

//
// Google Translation Plugin - maven plugin facilitating localization using google docs
// Copyright (c) 2014, Three Rings Design, Inc. - All rights reserved.
// http://github.com/threerings/gxlate-plugin/blob/master/LICENSE

package com.threerings.tools.gxlate.spreadsheet;

import java.util.List;
import java.util.Map;

import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gdata.data.spreadsheet.CellEntry;

/**
 * Allows access to the rows of a {@link Table} using a tuple of column values.
 */
public class Index {
    /**
     * A tuple of column values that supports equality, hashing and toString.
     */
    public static class Key {
        Key(List<String> fieldValues) {
            _fieldValues = fieldValues;
        }

        @Override
        public boolean equals(Object other) {
            return other instanceof Key && ((Key) other)._fieldValues.equals(_fieldValues);
        }

        @Override
        public int hashCode() {
            return _fieldValues.hashCode();
        }

        @Override
        public String toString() {
            return Joiner.on("::").join(_fieldValues);
        }

        List<String> _fieldValues;
    }

    /**
     * Base class for a problem that prevents correct indexing.
     */
    public static abstract class Error {
        abstract String message();
    }

    /**
     * Represents a duplicate key in a spreadsheet, contains the offending rows.
     */
    public static class DuplicateKeyError extends Error {
        public final int row1;
        public final int row2;

        DuplicateKeyError(Row row1, Row row2) {
            this.row1 = row1.getNum();
            this.row2 = row2.getNum();
        }

        @Override
        String message() {
            return "Duplicate key, on rows " + row1 + " and " + row2;
        }
    }

    /**
     * Represents a missing cell error, contains the offending row and the column header that is
     * missing.
     */
    public static class MissingCellError extends Error {
        public final int row;
        public final String header;

        MissingCellError(Row row, String header) {
            this.row = row.getNum();
            this.header = header;
        }

        @Override
        String message() {
            return "Cell missing, row " + row + ", column name " + header;
        }
    }

    /**
     * Thrown when a table could not be indexed.
     */
    public static class IndexError extends Exception {
        public final List<Error> errors;

        public IndexError(List<Error> errors) {
            super(message(errors));
            this.errors = errors;
        }

        private static String message(List<Error> errors) {
            if (errors.size() == 1) {
                return errors.get(0).message();
            }
            String message = "Errors occurred:\n";
            for (Error error : errors) {
                message += error.message() + "\n";
            }
            return message;
        }

        private static final long serialVersionUID = 1L;
    }

    /**
     * Creates a new index for the given table and columns. Index creation stops and an exception
     * is thrown after a fixed number of problems occur.
     * @throws IndexError if there were any problems creating the index.
     */
    public Index(Table table, String... headers) throws IndexError {
        _rows = Maps.newHashMapWithExpectedSize(table.getRows().size());
        _headers = headers;

        List<Error> errors = Lists.newArrayList();
        for (Row row : table.getRows()) {
            if (row == null) {
                continue;
            }
            Key key = key(row, errors);
            Row old = _rows.put(key, row);
            if (old != null) {
                errors.add(new DuplicateKeyError(old, row));
            }
            if (errors.size() > 10) {
                throw new IndexError(errors);
            }
        }
        if (!errors.isEmpty()) {
            throw new IndexError(errors);
        }
    }

    /**
     * Creates a key for the given field values.
     */
    public Key key(Map<String, String> fieldValues) {
        List<String> values = Lists.newArrayList();
        for (String header : _headers) {
            String value = fieldValues.get(header);
            values.add(Preconditions.checkNotNull(value));
        }
        return new Key(values);
    }

    /**
     * Finds the row belonging to the given key, or null if no such row exists.
     */
    public Row lookup(Key key) {
        Row row = _rows.get(key);
        if (row != null && row.isDeleted()) {
            _rows.remove(key);
            row = null;
        }
        return row;
    }

    private Key key(Row row, List<Error> errors) {
        List<String> key = Lists.newArrayListWithExpectedSize(_headers.length);
        for (String header : _headers) {
            CellEntry cell = row.getCellEntry(header);
            if (cell == null) {
                errors.add(new MissingCellError(row, header));
                continue;
            }
            key.add(cell.getCell().getValue());
        }
        return key.size() == _headers.length ? new Key(key) : null;
    }

    private Map<Key, Row> _rows;
    private String[] _headers;
}