Sejam bem vindos ao Preciso Estudar Sempre. Hoje falaremos sobre plugins no Eclipse e como é fácil construir um. Para que você entenda o que iremos abordar aqui, é necessário que tenha algum conhecimento sobre Java, OO e Eclipse.
Na sua opinião, antes de você ler o parágrafo acima, você achava que era possível a construção de plugins para o Eclipse ?
Sim ? Não ?
Você deve estar pensando agora: "Ainnn Joãooowww, essas empresas que fazem esses plugins, possuem super laboratórios com tecnologia super avançada, não dá pra eu fazer o meu plugin para Eclipse com o meu notebookizinho meia boca".
Então eu lhe respondo: "Você está errado meu amigo. É possível sim fazer seu próprio plugin e ainda lhe digo mais, é muito fácil".
Caso você esteja absurdamente ávido para respostas que irão mudar sua vida, clique aqui e baixe o projeto pronto.
O nosso plugin será simples, ele irá procurar por uma palavra específica em arquivos dentro de uma pasta e exibir os resultados em uma view.
Vamos por as mãos na massa ?
1 - Crie uma pasta chamada arquivos, no lugar onde você quiser. Lembre-se que será nesse lugar que o plugin irá realizar as buscas.
2 - Crie uma pasta chamada workspace. Essa pasta será o workspace de desenvolvimento do plugin.
3 - Abra o Eclipse apontando a pasta criada no passo 2 como workspace.
4 - Siga os seguinte passos: File > New > Other > Plug-in Project
Figura 1 - Criação do plugin |
Figura 2 - Nome do plugin |
Figura 3 - Content |
Figura 4 - Templates |
Figura 5 - Configurações principais |
Até agora não programamos nada e já criamos um plugin. Lembra que citei no início no post o quanto era fácil criar um plugin para eclipse ? Então, você vê agora na prática.
Vamos ver funcionando ?
Clique com o botão direito em cima do seu projeto e siga os seguintes passos: Clique com o botão direito > Run As > Eclipse Application.
Figura 6 - Executando o plugin |
Após o Eclipse ter sido aberto, seu plugin será executado. Caso a execução automática não esteja funcionando, siga os seguintes passos: Window > Show View > Other > Escolha sua categoria e duplo clique no plugin.
Lembra daquela categoria que eu tinha citado acima ? Então, ela entra agora. Quando a tela de views é aberta, a separação é feita por categorias e o nome que você escolheu estará lá.
O resultado da primeira execução é exibido abaixo.
Figura 7 - Resultado da primeira execução |
Classe Localizador
package precisoestudarsempreplugin.views;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Localizador {
public Map<String,List<String>> localizar(){
Map<String,List<String>> ocorrencias = new HashMap<String, List<String>>();
File folder = new File("C:\\arquivos");
for(File file : folder.listFiles()){
ocorrencias.put(file.getName(), this.analisarArquivo(file));
}
return ocorrencias;
}
private List<String> analisarArquivo(File file){
List<String> ocorrencias = new ArrayList<String>();
BufferedReader bufferedReader = null;
try{
int lineCount=0;
String palavraProcurada = "Preciso Estudar Sempre";
bufferedReader = new BufferedReader(new FileReader(file));
while(bufferedReader.ready()){
++lineCount;
String line = bufferedReader.readLine();
if(line.toLowerCase().contains(palavraProcurada.toLowerCase())){
ocorrencias.add(lineCount + ": " + line);
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return ocorrencias;
}
}
Agora, modifique o conteúdo da classe do plugin, no nosso caso é a classe MeuPlugin.
package precisoestudarsempreplugin.views;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.DrillDownAdapter;
import org.eclipse.ui.part.ViewPart;
/**
* This sample class demonstrates how to plug-in a new
* workbench view. The view shows data obtained from the
* model. The sample creates a dummy model on the fly,
* but a real implementation would connect to the model
* available either in this or another plug-in (e.g. the workspace).
* The view is connected to the model using a content provider.
* <p>
* The view uses a label provider to define how model
* objects should be presented in the view. Each
* view can present the same model objects using
* different labels and icons, if needed. Alternatively,
* a single label provider can be shared between views
* in order to ensure that objects of the same type are
* presented in the same way everywhere.
* <p>
*/
public class MeuPlugin extends ViewPart {
/**
* The ID of the view as specified by the extension.
*/
public static final String ID = "precisoestudarsempreplugin.views.MeuPlugin";
private TreeViewer viewer;
private DrillDownAdapter drillDownAdapter;
private Action action1;
private Action action2;
/*
* The content provider class is responsible for
* providing objects to the view. It can wrap
* existing objects in adapters or simply return
* objects as-is. These objects may be sensitive
* to the current input of the view, or ignore
* it and always show the same content
* (like Task List, for example).
*/
class TreeObject implements IAdaptable {
private String name;
private TreeParent parent;
public TreeObject(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setParent(TreeParent parent) {
this.parent = parent;
}
public TreeParent getParent() {
return parent;
}
public String toString() {
return getName();
}
public Object getAdapter(Class key) {
return null;
}
}
class TreeParent extends TreeObject {
private ArrayList children;
public TreeParent(String name) {
super(name);
children = new ArrayList();
}
public void addChild(TreeObject child) {
children.add(child);
child.setParent(this);
}
public void removeChild(TreeObject child) {
children.remove(child);
child.setParent(null);
}
public TreeObject [] getChildren() {
return (TreeObject [])children.toArray(new TreeObject[children.size()]);
}
public boolean hasChildren() {
return children.size()>0;
}
}
class ViewContentProvider implements IStructuredContentProvider,
ITreeContentProvider {
private TreeParent invisibleRoot;
public void inputChanged(Viewer v, Object oldInput, Object newInput) {
}
public void dispose() {
}
public Object[] getElements(Object parent) {
if (parent.equals(getViewSite())) {
if (invisibleRoot==null) initialize();
return getChildren(invisibleRoot);
}
return getChildren(parent);
}
public Object getParent(Object child) {
if (child instanceof TreeObject) {
return ((TreeObject)child).getParent();
}
return null;
}
public Object [] getChildren(Object parent) {
if (parent instanceof TreeParent) {
return ((TreeParent)parent).getChildren();
}
return new Object[0];
}
public boolean hasChildren(Object parent) {
if (parent instanceof TreeParent)
return ((TreeParent)parent).hasChildren();
return false;
}
/*
* We will set up a dummy model to initialize tree heararchy.
* In a real code, you will connect to a real model and
* expose its hierarchy.
*/
private void initialize() {
invisibleRoot = new TreeParent("");
Localizador localizador = new Localizador();
Map<String,List<String>> ocorrencias = localizador.localizar();
Set<String> keys = ocorrencias.keySet();
for(String key : keys){
if(!ocorrencias.get(key).isEmpty()){
TreeParent fileRoot = new TreeParent(key);
for(String ocorrencia : ocorrencias.get(key)){
TreeObject nodeOcorrencia = new TreeObject(ocorrencia);
fileRoot.addChild(nodeOcorrencia);
}
invisibleRoot.addChild(fileRoot);
}
}
}
}
class ViewLabelProvider extends LabelProvider {
public String getText(Object obj) {
return obj.toString();
}
public Image getImage(Object obj) {
String imageKey = ISharedImages.IMG_TOOL_FORWARD;
if (obj instanceof TreeParent)
imageKey = ISharedImages.IMG_OBJ_FILE;
return PlatformUI.getWorkbench().getSharedImages().getImage(imageKey);
}
}
class NameSorter extends ViewerSorter {
}
/**
* The constructor.
*/
public MeuPlugin() {
}
/**
* This is a callback that will allow us
* to create the viewer and initialize it.
*/
public void createPartControl(Composite parent) {
viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
drillDownAdapter = new DrillDownAdapter(viewer);
viewer.setContentProvider(new ViewContentProvider());
viewer.setLabelProvider(new ViewLabelProvider());
viewer.setSorter(new NameSorter());
viewer.setInput(getViewSite());
// Create the help context id for the viewer's control
PlatformUI.getWorkbench().getHelpSystem().setHelp(viewer.getControl(), "Teste.viewer");
makeActions();
hookContextMenu();
contributeToActionBars();
}
private void hookContextMenu() {
MenuManager menuMgr = new MenuManager("#PopupMenu");
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager manager) {
MeuPlugin.this.fillContextMenu(manager);
}
});
Menu menu = menuMgr.createContextMenu(viewer.getControl());
viewer.getControl().setMenu(menu);
getSite().registerContextMenu(menuMgr, viewer);
}
private void contributeToActionBars() {
IActionBars bars = getViewSite().getActionBars();
fillLocalPullDown(bars.getMenuManager());
fillLocalToolBar(bars.getToolBarManager());
}
private void fillLocalPullDown(IMenuManager manager) {
manager.add(action1);
manager.add(new Separator());
manager.add(action2);
}
private void fillContextMenu(IMenuManager manager) {
manager.add(action1);
manager.add(action2);
manager.add(new Separator());
drillDownAdapter.addNavigationActions(manager);
// Other plug-ins can contribute there actions here
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
private void fillLocalToolBar(IToolBarManager manager) {
manager.add(action1);
manager.add(action2);
manager.add(new Separator());
drillDownAdapter.addNavigationActions(manager);
}
private void makeActions() {
action1 = new Action() {
public void run() {
viewer.expandAll();
}
};
action1.setText("Expand All");
action1.setToolTipText("Expand All");
action1.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK));
action2 = new Action() {
public void run() {
viewer.collapseAll();
}
};
action2.setText("Collapse All");
action2.setToolTipText("Collapse All");
action2.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK));
}
/**
* Passing the focus request to the viewer's control.
*/
public void setFocus() {
viewer.getControl().setFocus();
}
}
Agora vamos entender o que está escrito.
- No método initialize é onde toda a magia acontece, é nele aonde são criados os nós da árvore (lembra o tipo de visualização que escolhemos ?). Cada nó possui o conteúdo que queremos exibir.
- As classes TreeParent e TreeObject são innerclasses, se você não sabe o que é isso, clique aqui e dê uma olhada.
- No método getImage definimos os ícones para as raízes e folhas da árvore. Os ícones usados no nosso plugin são os mesmos usados no Eclipse. Os ícones são representados através de constantes da classe ISharedImages.
- Nos métodos fillLocalPullDown e fillContextMenu é definido a disposição dos botões do plugin. A diferença entre esses métodos é que o primeiro se refere a seta ao lado do minimizar e o segundo aos botões da barra.
Figura 8 - Nossos botões |
- No método makeActions são definidos os tooltips, nomes, as ações e ícones dos botões. No nosso caso, os botões irão expandir e colapsar todos os resultados.
Referências:
Tutorial - Como criar um plug-in para o Eclipse: https://www.youtube.com/watch?v=4HubyNCIHg4
Nested Classes: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
4 comentários:
Maida, parabéns pelo post. Muito bacana saber que dá pra criar plugins no Eclipse.
Abs
Obrigado Luís.
Muito bom, parabéns!
Obrigado Wellington !!
Postar um comentário