/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* 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:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.actions.find;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.ide.actions.ActionManagerImpl;
import org.eclipse.che.ide.api.action.Action;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.action.ActionGroup;
import org.eclipse.che.ide.api.action.ActionManager;
import org.eclipse.che.ide.api.action.DefaultActionGroup;
import org.eclipse.che.ide.api.action.IdeActions;
import org.eclipse.che.ide.api.action.Presentation;
import org.eclipse.che.ide.api.action.Separator;
import org.eclipse.che.ide.api.mvp.Presenter;
import org.eclipse.che.ide.api.parts.PerspectiveManager;
import org.eclipse.che.ide.ui.toolbar.PresentationFactory;
import org.eclipse.che.ide.util.StringUtils;
import org.eclipse.che.ide.util.UnicodeUtils;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import static java.util.Collections.unmodifiableList;
/**
* @author Evgen Vidolob
* @author Dmitry Shnurenko
* @author Vlad Zhukovskyi
*/
@Singleton
public class FindActionPresenter implements Presenter, FindActionView.ActionDelegate {
private final PresentationFactory presentationFactory;
private final FindActionView view;
private final ActionManager actionManager;
private final Provider<PerspectiveManager> perspectiveManager;
private final Map<Action, String> actionsMap;
private final Comparator<Action> actionComparator = new Comparator<Action>() {
@Override
public int compare(Action o1, Action o2) {
int compare = compare(o1.getTemplatePresentation().getText(), o2.getTemplatePresentation().getText());
if (compare == 0 && !o1.equals(o2)) {
return o1.hashCode() - o2.hashCode();
}
return compare;
}
public int compare(@Nullable String o1, @Nullable String o2) {
if (o1 == null) return o2 == null ? 0 : -1;
if (o2 == null) return 1;
return o1.compareTo(o2);
}
};
@Inject
public FindActionPresenter(FindActionView view, ActionManager actionManager, Provider<PerspectiveManager> perspectiveManager) {
this.view = view;
this.actionManager = actionManager;
this.perspectiveManager = perspectiveManager;
view.setDelegate(this);
presentationFactory = new PresentationFactory();
actionsMap = new TreeMap<>(actionComparator);
}
@Override
public void go(AcceptsOneWidget container) {
}
public void show() {
view.show();
Action action = actionManager.getAction(IdeActions.GROUP_MAIN_MENU);
collectActions(actionsMap, (ActionGroup)action, action.getTemplatePresentation().getText());
view.focusOnInput();
if (view.getName() != null) {
nameChanged(view.getName(), view.getCheckBoxState());
}
}
private void collectActions(Map<Action, String> result, ActionGroup group, final String containingGroupName) {
final Action[] actions = group.getChildren(null);
includeGroup(result, group, actions, containingGroupName);
for (Action action : actions) {
if (action != null) {
if (action instanceof ActionGroup) {
final ActionGroup actionGroup = (ActionGroup)action;
final String groupName = actionGroup.getTemplatePresentation().getText();
collectActions(result, actionGroup,
StringUtils.isNullOrEmpty(groupName) || !actionGroup.isPopup() ? containingGroupName : groupName);
} else {
final String groupName = group.getTemplatePresentation().getText();
if (result.containsKey(action)) {
result.put(action, null);
} else {
result.put(action, StringUtils.isNullOrEmpty(groupName) ? containingGroupName : groupName);
}
}
}
}
}
private void includeGroup(Map<Action, String> result,
ActionGroup group,
Action[] actions,
String containingGroupName) {
boolean showGroup = true;
for (Action action : actions) {
if (actionManager.getId(action) != null) {
showGroup = false;
break;
}
}
if (showGroup) {
result.put(group, containingGroupName);
}
}
@Override
public void nameChanged(String name, boolean checkBoxState) {
if (name.isEmpty()) {
view.hideActions();
return;
}
String pattern = convertPattern(name.trim());
RegExp regExp = RegExp.compile(pattern);
Map<Action, String> actions = new TreeMap<>(actionComparator);
if (checkBoxState) {
Set<String> ids = ((ActionManagerImpl)actionManager).getActionIds();
for (Action action : actionsMap.keySet()) {
ids.remove(actionManager.getId(action));
}
for (String id : ids) {
Action action = actionManager.getAction(id);
Presentation presentation = action.getTemplatePresentation();
String text = presentation.getText();
if (text != null && regExp.test(text)) {
actions.put(action, null);
}
}
}
List<String> excludedActionIds = getExcludedActionIds(actionManager);
for (Entry<Action, String> entry : actionsMap.entrySet()) {
final Action action = entry.getKey();
final String groupName = entry.getValue();
if (excludedActionIds.contains(actionManager.getId(action))) {
continue;
}
Presentation presentation = action.getTemplatePresentation();
String text = presentation.getText();
if (text != null && regExp.test(text)) {
actions.put(action, groupName);
}
}
if (!actions.isEmpty()) {
view.showActions(actions);
} else {
view.hideActions();
}
}
@Override
public void onClose() {
actionsMap.clear();
}
@Override
public void onActionSelected(Action action) {
ActionEvent e = new ActionEvent(presentationFactory.getPresentation(action), actionManager, perspectiveManager.get());
action.update(e);
if (e.getPresentation().isEnabled() && e.getPresentation().isVisible()) {
view.hide();
action.actionPerformed(e);
}
}
private String convertPattern(String pattern) {
final int eol = pattern.indexOf('\n');
if (eol != -1) {
pattern = pattern.substring(0, eol);
}
if (pattern.length() >= 80) {
pattern = pattern.substring(0, 80);
}
final StringBuilder buffer = new StringBuilder();
boolean allowToLower = true;
if (containsOnlyUppercaseLetters(pattern)) {
allowToLower = false;
}
boolean firstIdentifierLetter = true;
for (int i = 0; i < pattern.length(); i++) {
final char c = pattern.charAt(i);
if (Character.isLetterOrDigit(c) || UnicodeUtils.regexpIdentifierOrWhitespace.test(String.valueOf(c))) {
// This logic allows to use uppercase letters only to catch the name like PDM for PsiDocumentManager
if (Character.isUpperCase(c) || Character.isDigit(c)) {
if (!firstIdentifierLetter) {
buffer.append("[^A-Z]*");
}
buffer.append("[");
buffer.append(c);
if (allowToLower || i == 0) {
buffer.append('|');
buffer.append(Character.toLowerCase(c));
}
buffer.append("]");
} else if (Character.isLowerCase(c)) {
buffer.append('[');
buffer.append(c);
buffer.append('|');
buffer.append(Character.toUpperCase(c));
buffer.append(']');
} else {
buffer.append(c);
}
firstIdentifierLetter = false;
} else if (c == '*') {
buffer.append(".*");
firstIdentifierLetter = true;
} else if (c == '.') {
buffer.append("\\.");
firstIdentifierLetter = true;
} else if (c == ' ') {
buffer.append("[^A-Z]*\\ ");
firstIdentifierLetter = true;
} else {
firstIdentifierLetter = true;
// for standard RegExp engine
buffer.append("\\u");
buffer.append(Integer.toHexString(c + 0x20000).substring(1));
// for OROMATCHER RegExp engine
// buffer.append("\\x");
// buffer.append(Integer.toHexString(c + 0x20000).substring(3));
}
}
return buffer.toString();
}
private static boolean containsOnlyUppercaseLetters(String s) {
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c != '*' && c != ' ' && !Character.isUpperCase(c)) return false;
}
return true;
}
private List<String> getExcludedActionIds(ActionManager actionManager) {
List<String> ids = new ArrayList<>();
DefaultActionGroup editGroup = (DefaultActionGroup)actionManager.getAction(IdeActions.GROUP_RECENT_FILES);
Action[] children = editGroup.getChildActionsOrStubs();
for (Action child : children) {
if (child instanceof Separator) {
continue;
}
ids.add(actionManager.getId(child));
}
return unmodifiableList(ids);
}
}