/*******************************************************************************
* MontiCore Language Workbench
* Copyright (c) 2015, 2016, MontiCore, All rights reserved.
*
* This project is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this project. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
package de.se_rwth.langeditor.modelpath;
import java.io.File;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Collectors;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ui.wizards.IClasspathContainerPage;
import org.eclipse.jdt.ui.wizards.IClasspathContainerPageExtension;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import com.google.common.collect.Lists;
import de.se_rwth.langeditor.global.Constants;
import de.se_rwth.langeditor.injection.DIService;
import de.se_rwth.langeditor.modelstates.ModelStateAssembler;
import de.se_rwth.langeditor.util.Misc;
/**
* The framework always calls the methods of this class in the order {@link #initialize},
* {@link #setSelection}, {@link #createControl} when the wizard is launched and {@link #finish},
* then {@link #getSelection} when the wizard finishes.
*
* @author Sebastian Oberhoff
*/
public class ModelPathContainerPage extends WizardPage implements IClasspathContainerPage,
IClasspathContainerPageExtension {
private Table table;
private IJavaProject javaProject;
private IClasspathEntry[] modelPathEntries;
private boolean isConfigurable;
public ModelPathContainerPage() {
super("Modelpath Wizard");
}
@Override
public void initialize(IJavaProject javaProject, IClasspathEntry[] currentEntries) {
this.javaProject = javaProject;
try {
modelPathEntries =
Optional.ofNullable(JavaCore.getClasspathContainer(Constants.MODELPATH, javaProject))
.map(IClasspathContainer::getClasspathEntries)
.orElse(new IClasspathEntry[] {});
}
catch (JavaModelException e) {
throw new RuntimeException(e);
}
}
@Override
public void setSelection(IClasspathEntry modelPathContainerEntry) {
try {
boolean modelPathAlreadySet = Arrays.stream(javaProject.getRawClasspath()).anyMatch(entry ->
entry.getPath().equals(Constants.MODELPATH));
boolean shouldCreateNewModelpath = modelPathContainerEntry == null;
isConfigurable = !(modelPathAlreadySet && shouldCreateNewModelpath);
}
catch (JavaModelException e) {
throw new RuntimeException(e);
}
}
@Override
public void createControl(Composite parent) {
Composite composite = createComposite(parent);
createButtons(composite);
table = createTable(composite);
setControl(composite);
}
@Override
public boolean finish() {
if (!isConfigurable) {
MessageDialog
.openError(
null,
"Modelpath already set",
"A new Modelpath can't be created while another Modelpath is already configured for the same project. Please use \"Configure Build Path\" instead.");
}
return isConfigurable;
}
@Override
public IClasspathEntry getSelection() {
IClasspathAttribute[] modelPathEntries = convertEntriesToAttributes();
IClasspathEntry modelPathContainerEntry = JavaCore.newContainerEntry(Constants.MODELPATH, null,
modelPathEntries, false);
updateClasspath(modelPathContainerEntry);
DIService.getInstance(ModelStateAssembler.class).scheduleFullRebuild();
return modelPathContainerEntry;
}
private IClasspathAttribute[] convertEntriesToAttributes() {
TableItem[] items = table.getItems();
return Arrays.stream(items)
.map(TableItem::getText)
.map(itemText -> JavaCore.newClasspathAttribute(
Constants.MODELPATH_ENTRY + File.separator + itemText, itemText))
.collect(Collectors.toList())
.toArray(new IClasspathAttribute[items.length]);
}
private void updateClasspath(IClasspathEntry newModelPathContainerEntry) {
boolean classpathModified = Misc.removeFromClasspath(javaProject, entry ->
entry.getPath().equals(Constants.MODELPATH));
if (classpathModified) {
Misc.addToClasspath(javaProject, Lists.newArrayList(newModelPathContainerEntry));
}
}
private Composite createComposite(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout(1, false);
composite.setLayout(layout);
return composite;
}
private void createButtons(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
RowLayout layout = new RowLayout(SWT.HORIZONTAL);
composite.setLayout(layout);
createNewFileEntryButton(composite);
createNewDirectoryEntryButton(composite);
createRemoveEntryButton(composite);
}
private void createNewFileEntryButton(Composite parent) {
Button newEntryButton = new Button(parent, SWT.NONE);
newEntryButton.setText("Add file entry");
newEntryButton.addMouseListener(new MouseListener() {
@Override
public void mouseDoubleClick(MouseEvent e) {
// no op
}
@Override
public void mouseDown(MouseEvent e) {
// no op
}
@Override
public void mouseUp(MouseEvent e) {
FileDialog dialog = new FileDialog(parent.getShell(), parent.getStyle());
String selection = dialog.open();
createTableItem(table, selection);
table.getColumn(0).pack();
}
});
}
private void createNewDirectoryEntryButton(Composite parent) {
Button newEntryButton = new Button(parent, SWT.NONE);
newEntryButton.setText("Add directory entry");
newEntryButton.addMouseListener(new MouseListener() {
@Override
public void mouseDoubleClick(MouseEvent e) {
// no op
}
@Override
public void mouseDown(MouseEvent e) {
// no op
}
@Override
public void mouseUp(MouseEvent e) {
DirectoryDialog dialog = new DirectoryDialog(parent.getShell(), parent.getStyle());
String selection = dialog.open();
createTableItem(table, selection);
table.getColumn(0).pack();
}
});
}
private void createRemoveEntryButton(Composite parent) {
Button removeEntryButton = new Button(parent, SWT.NONE);
removeEntryButton.setText("Remove entry");
removeEntryButton.addMouseListener(new MouseListener() {
@Override
public void mouseDoubleClick(MouseEvent e) {
// no op
}
@Override
public void mouseDown(MouseEvent e) {
// no op
}
@Override
public void mouseUp(MouseEvent e) {
for (int selectionIndex : table.getSelectionIndices()) {
table.remove(selectionIndex);
}
}
});
}
private Table createTable(Composite parent) {
Table table = new Table(parent, SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION);
table.setHeaderVisible(true);
GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
table.setLayoutData(data);
TableColumn column = new TableColumn(table, SWT.NONE);
column.setText("Modelpath entries");
for (IClasspathEntry classpathEntry : modelPathEntries) {
createTableItem(table, classpathEntry.getPath().toOSString());
}
column.pack();
return table;
}
private void createTableItem(Table parent, String itemText) {
TableItem tableItem = new TableItem(parent, SWT.NONE);
tableItem.setText(itemText);
Misc.loadImage("icons/jar_l_obj.gif").ifPresent(tableItem::setImage);
}
}