/**
* Copyright (c) 2011 Stefan Henss.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Stefan Henss - initial API and implementation.
* Olav Lenz - externalize Strings.
*/
package org.eclipse.recommenders.internal.apidocs.rcp;
import static com.google.common.collect.Collections2.filter;
import static com.google.common.collect.Lists.newLinkedList;
import static java.text.MessageFormat.format;
import static java.util.Arrays.asList;
import static org.eclipse.recommenders.internal.apidocs.rcp.ApidocsViewUtils.*;
import static org.eclipse.recommenders.utils.Bags.newHashMultiset;
import static org.eclipse.swt.SWT.COLOR_INFO_FOREGROUND;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import javax.inject.Inject;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.recommenders.apidocs.ClassOverrideDirectives;
import org.eclipse.recommenders.apidocs.ClassOverridePatterns;
import org.eclipse.recommenders.apidocs.MethodPattern;
import org.eclipse.recommenders.apidocs.OverrideDirectivesModelProvider;
import org.eclipse.recommenders.apidocs.OverridePatternsModelProvider;
import org.eclipse.recommenders.apidocs.rcp.ApidocProvider;
import org.eclipse.recommenders.apidocs.rcp.JavaSelectionSubscriber;
import org.eclipse.recommenders.internal.apidocs.rcp.l10n.Messages;
import org.eclipse.recommenders.models.IInputStreamTransformer;
import org.eclipse.recommenders.models.IModelIndex;
import org.eclipse.recommenders.models.IModelRepository;
import org.eclipse.recommenders.models.rcp.IProjectCoordinateProvider;
import org.eclipse.recommenders.models.rcp.ModelEvents.ModelIndexOpenedEvent;
import org.eclipse.recommenders.rcp.JavaElementResolver;
import org.eclipse.recommenders.rcp.JavaElementSelectionEvent;
import org.eclipse.recommenders.utils.names.IMethodName;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.google.common.collect.Multiset;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
public final class OverridesProvider extends ApidocProvider {
private final JavaElementResolver resolver;
private final EventBus workspaceBus;
private final IProjectCoordinateProvider pcProvider;
private final OverrideDirectivesModelProvider dStore;
private final OverridePatternsModelProvider pStore;
@Inject
public OverridesProvider(IProjectCoordinateProvider pcProvider, JavaElementResolver resolver,
EventBus workspaceBus, IModelRepository repository, IModelIndex index,
Map<String, IInputStreamTransformer> transformers) {
this.pcProvider = pcProvider;
this.resolver = resolver;
this.workspaceBus = workspaceBus;
this.pStore = new OverridePatternsModelProvider(repository, index, transformers);
this.dStore = new OverrideDirectivesModelProvider(repository, index, transformers);
}
@Subscribe
public void onEvent(ModelIndexOpenedEvent e) throws IOException {
pStore.close();
pStore.open();
dStore.close();
dStore.open();
}
@JavaSelectionSubscriber
public void onTypeRootSelection(final ITypeRoot root, final JavaElementSelectionEvent event, final Composite parent)
throws ExecutionException {
final IType type = root.findPrimaryType();
if (type != null) {
onTypeSelection(type, event, parent);
}
}
@JavaSelectionSubscriber
public void onMethodSelection(final IMethod method, final JavaElementSelectionEvent event, final Composite parent)
throws ExecutionException {
onTypeSelection(method.getDeclaringType(), event, parent);
}
@JavaSelectionSubscriber
public void onVariableSelection(ILocalVariable var, JavaElementSelectionEvent event, Composite parent)
throws ExecutionException {
IType type = ApidocsViewUtils.findType(var).orNull();
if (type != null) {
onTypeSelection(type, event, parent);
}
}
@JavaSelectionSubscriber
public void onVariableSelection(IField var, JavaElementSelectionEvent event, Composite parent)
throws ExecutionException, JavaModelException {
IType type = ApidocsViewUtils.findType(var).orNull();
if (type != null) {
onTypeSelection(type, event, parent);
}
}
@JavaSelectionSubscriber
public void onTypeSelection(final IType type, final JavaElementSelectionEvent event, final Composite parent)
throws ExecutionException {
renderClassOverrideDirectives(type, parent);
renderClassOverridesPatterns(type, parent);
}
private boolean renderClassOverrideDirectives(final IType type, final Composite parent) throws ExecutionException {
ClassOverrideDirectives model = dStore.acquireModel(pcProvider.toUniqueName(type).orNull()).orNull();
try {
if (model == null || model.getOverrides() == null) {
return false;
}
runSyncInUiThread(new TypeOverrideDirectivesRenderer(type, model, parent));
} finally {
dStore.releaseModel(model);
}
return true;
}
private boolean renderClassOverridesPatterns(final IType type, final Composite parent) throws ExecutionException {
ClassOverridePatterns opt = pStore.acquireModel(pcProvider.toUniqueName(type).orNull()).orNull();
try {
if (opt != null) {
runSyncInUiThread(new OverridePatternsRenderer(type, opt, parent));
}
} finally {
pStore.releaseModel(opt);
}
return true;
}
// ========================================================================
// TODO: Review the renderer code is redundant and needs refactoring after
// all providers have been written to
// identify more common parts.
private class TypeOverrideDirectivesRenderer implements Runnable {
private final IType type;
private final ClassOverrideDirectives directive;
private final Composite parent;
private Composite container;
public TypeOverrideDirectivesRenderer(final IType type, final ClassOverrideDirectives directive,
final Composite parent) {
this.type = type;
this.directive = directive;
this.parent = parent;
}
@Override
public void run() {
createContainer();
addHeader();
addDirectives();
}
private void createContainer() {
container = new Composite(parent, SWT.NONE);
setInfoBackgroundColor(container);
container.setLayout(new GridLayout());
}
private void addHeader() {
final String message = format(Messages.PROVIDER_INTRO_OVERRIDE_STATISTICS,
directive.getNumberOfSubclasses(), type.getElementName());
Label label = new Label(container, SWT.NONE);
label.setText(message);
setInfoForegroundColor(label);
setInfoBackgroundColor(label);
}
private void addDirectives() {
final int numberOfSubclasses = directive.getNumberOfSubclasses();
final Multiset<IMethodName> b = newHashMultiset(directive.getOverrides());
renderMethodDirectivesBlock(container, b, numberOfSubclasses, workspaceBus, resolver,
Messages.TABLE_CELL_RELATION_OVERRIDE);
}
}
private class OverridePatternsRenderer implements Runnable {
private final Composite parent;
private Composite container;
private double totalNumberOfExamples;
private List<MethodPattern> patterns;
public OverridePatternsRenderer(final IType type, final ClassOverridePatterns directive, final Composite parent) {
this.parent = parent;
setPatterns(directive);
computeTotalNumberOfExamples();
filterInfrequentPatterns();
sortPatterns();
}
private void setPatterns(final ClassOverridePatterns patterns) {
this.patterns = asList(patterns.getPatterns());
}
private void filterInfrequentPatterns() {
patterns = newLinkedList(filter(patterns, new Predicate<MethodPattern>() {
@Override
public boolean apply(final MethodPattern input) {
final int numberOfObservations = input.getNumberOfObservations();
return numberOfObservations / totalNumberOfExamples > 0.1;
}
}));
}
private void sortPatterns() {
Collections.sort(patterns, new Comparator<MethodPattern>() {
@Override
public int compare(final MethodPattern o1, final MethodPattern o2) {
return o2.getNumberOfObservations() - o1.getNumberOfObservations();
}
});
}
private void computeTotalNumberOfExamples() {
for (final MethodPattern pattern : patterns) {
totalNumberOfExamples += pattern.getNumberOfObservations();
}
}
@Override
public void run() {
createContainer();
addHeader();
int i = 1;
for (final MethodPattern pattern : patterns) {
addDirectives(pattern, i++);
}
}
private void createContainer() {
container = new Composite(parent, SWT.NONE);
setInfoBackgroundColor(container);
container.setLayout(new GridLayout());
container.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
}
private void addHeader() {
new Label(container, SWT.None);
final String message = format(Messages.PROVIDER_INTRO_OVERRIDE_PATTERNS);
createLabel(container, message, true);
}
private void addDirectives(final org.eclipse.recommenders.apidocs.MethodPattern pattern, final int index) {
final double patternPercentage = pattern.getNumberOfObservations() / totalNumberOfExamples;
final String text = format(Messages.TABLE_HEADER_OVERRIDE_PATTERN, index, patternPercentage,
pattern.getNumberOfObservations());
createLabel(container, text, true, false, SWT.COLOR_DARK_GRAY, true);
final Composite group = createGridComposite(container, 1, 0, 0, 0, 0);
final List<Entry<IMethodName, Double>> s = Lists.newLinkedList(pattern.getMethods().entrySet());
Collections.sort(s, new Comparator<Entry<IMethodName, Double>>() {
@Override
public int compare(final Entry<IMethodName, Double> o1, final Entry<IMethodName, Double> o2) {
// return o2.getValue().compareTo(o1.getValue());
return o1.getKey().getName().compareTo(o2.getKey().getName());
}
});
final Table table = new Table(group, SWT.NONE | SWT.HIDE_SELECTION);
table.setBackground(createColor(SWT.COLOR_INFO_BACKGROUND));
table.setLayoutData(GridDataFactory.fillDefaults().indent(10, 0).create());
final TableColumn column1 = new TableColumn(table, SWT.NONE);
final TableColumn column2 = new TableColumn(table, SWT.NONE);
final TableColumn column3 = new TableColumn(table, SWT.NONE);
final TableColumn column4 = new TableColumn(table, SWT.NONE);
for (final Entry<IMethodName, Double> entry : s) {
final double percentage = entry.getValue();
final String phraseText = percentageToRecommendationPhrase((int) Math.rint(percentage * 100));
final String stats = format(Messages.TABLE_CELL_SUFFIX_PERCENTAGE, percentage);
final Link bar = createMethodLink(table, entry.getKey(), resolver, workspaceBus);
final TableItem item = new TableItem(table, SWT.NONE);
item.setText(new String[] { phraseText, Messages.TABLE_CELL_RELATION_OVERRIDE, bar.getText(), stats });
item.setFont(0, JFaceResources.getBannerFont());
item.setForeground(createColor(COLOR_INFO_FOREGROUND));
final TableEditor editor = new TableEditor(table);
editor.grabHorizontal = editor.grabVertical = true;
editor.setEditor(bar, item, 2);
}
column1.pack();
column2.pack();
column3.pack();
column4.pack();
new Label(container, SWT.SEPARATOR | SWT.HORIZONTAL);
}
}
}