Let's first create a class Tabela
:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
class Tabela<L, C, X> {
private final Set<C> colunas;
private final Set<L> linhas;
private final Map<L, Map<C, X>> celulas;
private final Function<? super L, String> strLinhas;
private final Function<? super C, String> strColunas;
private final Function<? super X, String> strCelulas;
@FunctionalInterface
public static interface TabelaConsumer<L, C, X> {
public void accept(L linha, C coluna, X celula);
}
public Tabela(
Collection<L> linhas,
Collection<C> colunas,
Function<? super L, String> strLinhas,
Function<? super C, String> strColunas,
Function<? super X, String> strCelulas)
{
this.colunas = new LinkedHashSet<>(colunas);
this.linhas = new LinkedHashSet<>(linhas);
this.strLinhas = strLinhas;
this.strColunas = strColunas;
this.strCelulas = strCelulas;
this.celulas = new LinkedHashMap<>(this.linhas.size());
for (L linha : this.linhas) {
Map<C, X> valoresLinha = new LinkedHashMap<>(this.colunas.size());
for (C coluna : this.colunas) {
valoresLinha.put(coluna, null);
}
this.celulas.put(linha, valoresLinha);
}
}
public void put(L linha, C coluna, X valor) {
if (!linhas.contains(linha) || !colunas.contains(coluna)) throw new IllegalArgumentException();
celulas.get(linha).put(coluna, valor);
}
public X get(L linha, C coluna) {
if (!linhas.contains(linha) || !colunas.contains(coluna)) return null;
return celulas.get(linha).get(coluna);
}
public void forEach(TabelaConsumer<? super L, ? super C, ? super X> consumer) {
for (L linha : linhas) {
for (C coluna : colunas) {
consumer.accept(linha, coluna, get(linha, coluna));
}
}
}
private List<Integer> calcularLargura() {
List<Integer> lista = new ArrayList<>(colunas.size() + 1);
int m = 0;
for (L linha : linhas) {
int x = strLinhas.apply(linha).length();
if (x > m) m = x;
}
lista.add(m);
for (C coluna : colunas) {
int n = strColunas.apply(coluna).length();
for (L linha : linhas) {
X celula = get(linha, coluna);
int x = celula == null ? 0 : strCelulas.apply(celula).length();
if (x > n) n = x;
}
lista.add(n);
}
return lista;
}
private void imprimirSeparador(StringBuilder sb, int tamanho) {
sb.append("+-");
for (int i = 0; i <= tamanho; i++) {
sb.append('-');
}
}
private void imprimirSeparador(StringBuilder sb, List<Integer> tamanhos) {
for (int i : tamanhos) {
imprimirSeparador(sb, i);
}
sb.append("+\n");
}
private void imprimirCelula(StringBuilder sb, int tamanho, String conteudo) {
sb.append("| ").append(conteudo);
for (int i = conteudo.length(); i <= tamanho; i++) {
sb.append(' ');
}
}
private void imprimirCabecalho(StringBuilder sb, List<Integer> tamanhos) {
Iterator<Integer> it = tamanhos.iterator();
imprimirCelula(sb, it.next(), "");
for (C coluna : colunas) {
imprimirCelula(sb, it.next(), strColunas.apply(coluna));
}
sb.append("|\n");
}
private void imprimirLinha(StringBuilder sb, List<Integer> tamanhos, L linha) {
Iterator<Integer> it = tamanhos.iterator();
imprimirCelula(sb, it.next(), strLinhas.apply(linha));
for (C coluna : colunas) {
X celula = get(linha, coluna);
imprimirCelula(sb, it.next(), celula == null ? "" : strCelulas.apply(celula));
}
sb.append("|\n");
}
@Override
public String toString() {
List<Integer> tamanhos = calcularLargura();
int larguraTotal = tamanhos.stream().reduce(0, Integer::sum) + 3 * tamanhos.size() + 2;
StringBuilder sb = new StringBuilder(larguraTotal * (linhas.size() * 2 + 3));
imprimirSeparador(sb, tamanhos);
imprimirCabecalho(sb, tamanhos);
imprimirSeparador(sb, tamanhos);
for (L linha : linhas) {
imprimirLinha(sb, tamanhos, linha);
imprimirSeparador(sb, tamanhos);
}
return sb.toString();
}
}
This class is generic. Having it, then we will use the Local
and Atividade
classes to define, as well as the java.time.LocalDate
class for the contents of the table:
class Local {
private final String nome;
public Local(String nome) {
this.nome = nome;
}
@Override
public String toString() {
return nome;
}
}
class Atividade {
private final String nome;
public Atividade(String nome) {
this.nome = nome;
}
@Override
public String toString() {
return nome;
}
}
Finally, the code that uses all this, creating table 1 and transforming it into table 2:
class Teste {
private static final DateTimeFormatter FMT = DateTimeFormatter
.ofPattern("dd/MM/uuuu")
.withResolverStyle(ResolverStyle.STRICT);
public static void main(String[] args) {
// Define os locais e atividades.
Local casa = new Local("Casa");
Local escola = new Local("Escola");
Local trabalho = new Local("Trabalho");
Atividade estudo = new Atividade("Estudo");
Atividade treino = new Atividade("Treino");
Atividade apresentacao = new Atividade("Apresentação");
// Define a estrutura da tabela 1.
Tabela<Atividade, Local, LocalDate> tabela1 = new Tabela<>(
Arrays.asList(estudo, treino, apresentacao),
Arrays.asList(casa, escola, trabalho),
Object::toString,
Object::toString,
ld -> ld.format(FMT)
);
// Preenche a tabela 1.
tabela1.put(estudo, casa, LocalDate.of(2017, 2, 1));
tabela1.put(estudo, escola, LocalDate.of(2017, 3, 10));
tabela1.put(estudo, trabalho, LocalDate.of(2017, 1, 1));
tabela1.put(treino, casa, LocalDate.of(2017, 5, 3));
tabela1.put(treino, escola, LocalDate.of(2017, 2, 4));
tabela1.put(treino, trabalho, LocalDate.of(2017, 5, 1));
tabela1.put(apresentacao, casa, LocalDate.of(2017, 2, 2));
tabela1.put(apresentacao, escola, LocalDate.of(2017, 3, 3));
tabela1.put(apresentacao, trabalho, LocalDate.of(2017, 12, 10));
// Mostra a tabela 1.
System.out.println("Tabela 1:");
System.out.println(tabela1);
// Descobre as datas que são linhas da tabela 2.
Set<LocalDate> datas = new TreeSet<>();
tabela1.forEach((atividade, local, data) -> datas.add(data));
// Define a estrutura da tabela 2.
Tabela<LocalDate, Atividade, Local> tabela2 = new Tabela<>(
datas,
Arrays.asList(estudo, treino, apresentacao),
ld -> ld.format(FMT),
Object::toString,
Object::toString
);
// Preenche a tabela 2 com base nos dados da tabela 1.
tabela1.forEach((atividade, local, data) -> tabela2.put(data, atividade, local));
// Mostra a tabela 2.
System.out.println("Tabela 2:");
System.out.println(tabela2);
}
}
Here's the output:
Tabela 1:
+--------------+------------+------------+------------+
| | Casa | Escola | Trabalho |
+--------------+------------+------------+------------+
| Estudo | 01/02/2017 | 10/03/2017 | 01/01/2017 |
+--------------+------------+------------+------------+
| Treino | 03/05/2017 | 04/02/2017 | 01/05/2017 |
+--------------+------------+------------+------------+
| Apresentação | 02/02/2017 | 03/03/2017 | 10/12/2017 |
+--------------+------------+------------+------------+
Tabela 2:
+------------+----------+----------+--------------+
| | Estudo | Treino | Apresentação |
+------------+----------+----------+--------------+
| 01/01/2017 | Trabalho | | |
+------------+----------+----------+--------------+
| 01/02/2017 | Casa | | |
+------------+----------+----------+--------------+
| 02/02/2017 | | | Casa |
+------------+----------+----------+--------------+
| 04/02/2017 | | Escola | |
+------------+----------+----------+--------------+
| 03/03/2017 | | | Escola |
+------------+----------+----------+--------------+
| 10/03/2017 | Escola | | |
+------------+----------+----------+--------------+
| 01/05/2017 | | Trabalho | |
+------------+----------+----------+--------------+
| 03/05/2017 | | Casa | |
+------------+----------+----------+--------------+
| 10/12/2017 | | | Trabalho |
+------------+----------+----------+--------------+
See working here on ideone.