Não tenho como dizer ser há alguma em específico que realize a impressão de todos os códigos fonte em um clique.
A ferramenta pronta também desconheço, mas é possível “varrer” as pastas do projeto procurando por arquivos .java e por semelhança, poderia ser feito o mesmo para outros tipos de arquivo.
Não posso dizer que fiz testes suficientes e tenho baixa expectativa quanto a sua eficiência por este motivo.
Achei legal a implementação e vou compartilhar por ser uma forma de aprendizado, assim, não cabe o utilizar para fins de trabalho, por questões de integridade.
O eixo principal da movimentação entre as pastas e arquivos é o método a seguir, digamos que ele seja o core:
//usado para separar pastas e agrupar arquivos.java
private void percorrerDiretorio(Path caminho) {
try {
//separa diretórios de arquivos
Files.list(caminho).forEach(arquivo -> {
if (arquivo.toFile().isDirectory()) {
percorrerDiretorio(arquivo);
} else if (arquivo.toString().endsWith(".java")) {
gerenciarDiretorios(caminho.toFile().getAbsolutePath()).getArquivosJava().add(new ArquivoJava(arquivo));
}
});
} catch (IOException ex) {
Logger.getLogger(OrdenadorJava.class.getName()).log(Level.SEVERE, null, ex);
logExcessoes.add(new LogExcessoes(ex.getMessage()));
}
}
Eis as classes:
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
public class Efemera {
//"imprimindo" os arquivos .java
public static void main(String[] args) {
JOptionPane.showMessageDialog(null, "Nas configurações atuais, pacotes que não contenham\n"
+ "arquivos.java, terão o seu caminho ignorados");
JOptionPane.showMessageDialog(null, "Lembre de SALVAR o Projeto antes de imprimir.\nInforme a pasta onde estão os códigos fonte");
JFileChooser jfc = new JFileChooser();
jfc.setDialogTitle("Escolha a pasta source");
jfc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
if (jfc.showOpenDialog(jfc) == JFileChooser.APPROVE_OPTION) {
OrdenadorJava ordenador = new OrdenadorJava(jfc.getSelectedFile().toPath());
ordenador.getDiretorios().forEach(diretorio -> {
System.out.println("===================================================\nPACOTE:\n"
+ diretorio.getCaminho()
+ "\n===================================================\n"
+ "Total de arquivos.java no diretório: "
+ diretorio.getArquivosJava().size()
+ "\nArquivos relacionados: ");
diretorio.getArquivosJava().forEach(arquivo -> System.out.println(arquivo.getCaminho().getFileName()));
System.out.println("Imprimindo as classes\n\n");
diretorio.getArquivosJava()
.stream()
.map(ArquivoJava::getLinhas)
//imprimindo os arquivos.java
.forEach(lista -> lista.forEach(System.out::println));
});
if (ordenador.getDiretorios().isEmpty()) {
JOptionPane.showMessageDialog(null, "Não foram encontrados arquivos.java");
}
}
}
}
.
import java.util.ArrayList;
public final class Diretorio {
private final String caminho;
private final ArrayList<ArquivoJava> arquivosJava = new ArrayList<>();
public Diretorio(String diretorio) {
this.caminho = diretorio;
}
public ArrayList<ArquivoJava> getArquivosJava() {
return arquivosJava;
}
public String getCaminho() {
return caminho;
}
}
.
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.swing.JOptionPane;
//classe que fica
public final class ArquivoJava {
private Path caminho;
private List<String> linhas;
private final List<LogExcessoes> logDeExcessoes = new ArrayList<>();
//previsão de nullPointerException caso as linhas não possam ser atribuidas
public ArquivoJava(Path caminho) {
try {
linhas = Files.readAllLines(caminho, StandardCharsets.UTF_8);
this.caminho = caminho;
} catch (IOException ex) {
Logger.getLogger(ArquivoJava.class.getName()).log(Level.SEVERE, null, ex);
JOptionPane.showMessageDialog(null, caminho.toString());
logDeExcessoes.add(new LogExcessoes(ex.getMessage()));
}
}
public List<LogExcessoes> getLogDeExcessoes() {
return logDeExcessoes;
}
public List<String> getLinhas() {
return linhas;
}
public Path getCaminho() {
return caminho;
}
public String tipo() {
String[] tipos = {"abstract", "class", "@interface", "interface", "enum"};
return Arrays.stream(tipos).filter(tipo -> caminho.toString().contains(tipo)).findFirst().orElse("O arquivo é inconsistente");
}
public int totalIfRelativo() {
int relativa = (int) linhas.stream().filter(linha -> !linha.contains("\"") && linha.contains("?") && linha.contains(":")).count();
return Collections.frequency(linhas, " if(") + relativa;
}
public int totalForRelativo() {
return Collections.frequency(linhas, " for(") + Collections.frequency(linhas, "foreach(");
}
public int totalWhileRelativo() {
return Collections.frequency(linhas, " while(");
}
public List<String> metodosPrivados() {
return linhas.stream().filter(linha -> linha.contains("private ") && linha.contains("){")).collect(Collectors.toList());
}
public List<String> atributosPrivados() {
return linhas.stream().filter(linha -> linha.contains("private ") && !linha.contains("){")).collect(Collectors.toList());
}
}
.
import java.util.ArrayList;
import java.util.Calendar;
//apenas para fins de evolução da aplicação
public final class LogExcessoes {
private final String mensagem;
private final ArrayList<String> tratamentos = new ArrayList<>();
public LogExcessoes(String mensagem) {
this.mensagem = mensagem;
}
public String getMensagem() {
return mensagem;
}
public ArrayList<String> getTratamentos() {
return tratamentos;
}
public void adicionarTratamento(String tratamentoRealizado){
tratamentos.add(Calendar.getInstance()+" - "+tratamentoRealizado);
}
}
.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;
//Esta classe fica com a responsabilidade de pesquisar os arquivos.java e os organizar por diretórios
public final class OrdenadorJava {
private final ArrayList<Diretorio> diretorios = new ArrayList<>();
private final ArrayList<LogExcessoes> logExcessoes = new ArrayList<>();
public OrdenadorJava(Path pastaRaiz) {
percorrerDiretorio(pastaRaiz);
Collections.unmodifiableCollection(diretorios);
Collections.unmodifiableCollection(logExcessoes);
}
public ArrayList<Diretorio> getDiretorios() {
return diretorios;
}
public ArrayList<LogExcessoes> getLogExcessoes() {
return logExcessoes;
}
//usado para separar pastas e agrupar arquivos.java
private void percorrerDiretorio(Path caminho) {
try {
//separa diretórios de arquivos
Files.list(caminho).forEach(arquivo -> {
if (arquivo.toFile().isDirectory()) {
percorrerDiretorio(arquivo);
} else if (arquivo.toString().endsWith(".java")) {
gerenciarDiretorios(caminho.toFile().getAbsolutePath()).getArquivosJava().add(new ArquivoJava(arquivo));
}
});
} catch (IOException ex) {
Logger.getLogger(OrdenadorJava.class.getName()).log(Level.SEVERE, null, ex);
logExcessoes.add(new LogExcessoes(ex.getMessage()));
}
}
//o atributo diretorios só realiza a adição de diretórios inéditos
private Diretorio gerenciarDiretorios(String caminho) {
if (!diretorios.stream().anyMatch(diretorio -> diretorio.getCaminho().equals(caminho))) {
Diretorio retorno = new Diretorio(caminho);
diretorios.add(retorno);
return retorno;
}
return diretorios.stream().filter(diretorio -> diretorio.getCaminho().equals(caminho)).findFirst().orElse(null);
}
}
Estas classes fazem uso da recursividade para encontrar os arquivos.java.
Se perceber, vai ver vários métodos sem utilidade, mas eles podem vir a ter utilidade futuramente.
No que se refere as vantagens:
1 - esta implementação pode ser usada sem depender de uma IDE, setando apenas a pasta que contém os códigos fonte;
2 - Evoluindo a codificação:
I) pode ser integrado com ferramentas como o iText, imprimindo os códigos fonte em pdf com formatação personalizada, facilitando a sumarização de arquivos por pastas;
II) em vez de imprimir todo o projeto será possível imprimir apenas uma pasta ou um conjunto de pastas.
III) realizar estatísticas básicas (motivo da inclusão dos métodos desnecessários).
Desvantagens: é um protótipo para o qual não há expectativa na evolução da aplicação, além de falta de refatoração.
Por que implementar desta forma?
Por que as aplicações devem ser pensadas para evoluir.
Té+