| /* |
| * Copyright 2002-2005 Sascha Weinreuter |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.intellij.plugins.xpathView.ui; |
| |
| import com.intellij.codeInsight.intention.IntentionAction; |
| import com.intellij.ide.DataManager; |
| import com.intellij.javaee.ExternalResourceManager; |
| import com.intellij.openapi.actionSystem.CommonDataKeys; |
| import com.intellij.openapi.actionSystem.DataContext; |
| import com.intellij.openapi.actionSystem.PlatformDataKeys; |
| import com.intellij.openapi.fileTypes.LanguageFileType; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.ui.DialogWrapper; |
| import com.intellij.openapi.ui.Messages; |
| import com.intellij.openapi.ui.Splitter; |
| import com.intellij.openapi.util.DimensionService; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.psi.PsiElement; |
| import com.intellij.psi.PsiReference; |
| import com.intellij.psi.xml.XmlElement; |
| import com.intellij.ui.AnActionButton; |
| import com.intellij.ui.AnActionButtonRunnable; |
| import com.intellij.ui.IdeBorderFactory; |
| import com.intellij.ui.ToolbarDecorator; |
| import com.intellij.util.containers.BidirectionalMap; |
| import com.intellij.util.ui.PlatformColors; |
| import com.intellij.util.ui.Table; |
| import com.intellij.util.ui.UIUtil; |
| import org.intellij.lang.xpath.XPathFileType; |
| import org.intellij.lang.xpath.context.*; |
| import org.intellij.lang.xpath.psi.XPathElement; |
| import org.intellij.plugins.xpathView.util.MyPsiUtil; |
| import org.intellij.plugins.xpathView.util.Namespace; |
| import org.intellij.plugins.xpathView.util.Variable; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import javax.swing.table.AbstractTableModel; |
| import javax.swing.table.DefaultTableCellRenderer; |
| import javax.swing.table.TableCellEditor; |
| import javax.xml.namespace.QName; |
| import java.awt.*; |
| import java.util.*; |
| import java.util.List; |
| |
| import static org.intellij.plugins.xpathView.util.Copyable.Util.copy; |
| |
| public class EditContextDialog extends DialogWrapper { |
| private final DimensionService myDimensionService = DimensionService.getInstance(); |
| |
| private final Set<String> myUnresolvedPrefixes; |
| |
| private final JTable myVariableTable; |
| private final VariableTableModel myVariableTableModel; |
| |
| private final JTable myNamespaceTable; |
| private final NamespaceTableModel myNamespaceTableModel; |
| private final ContextProvider myContextProvider; |
| private Splitter mySplitter; |
| |
| public EditContextDialog(Project project, |
| Set<String> unresolvedPrefixes, |
| Collection<Namespace> namespaces, |
| Collection<Variable> variables, |
| ContextProvider contextProvider) { |
| super(project, false); |
| myUnresolvedPrefixes = unresolvedPrefixes; |
| |
| setTitle("Edit XPath Context"); |
| setModal(true); |
| |
| myContextProvider = new MyContextProvider(contextProvider); |
| |
| final List<Variable> m = copy(variables); |
| myVariableTableModel = new VariableTableModel(m, project, XPathFileType.XPATH); |
| myVariableTable = new Table(myVariableTableModel); |
| myVariableTable.setDefaultRenderer(String.class, new VariableCellRenderer(m)); |
| myVariableTable.setDefaultRenderer(Expression.class, new ExpressionCellRenderer(project)); |
| myVariableTable.setDefaultEditor(Expression.class, new ExpressionCellEditor(project)); |
| |
| int width = new JLabel("Name").getPreferredSize().width; |
| myVariableTable.getColumnModel().getColumn(0).setMinWidth(width); |
| myVariableTable.getColumnModel().getColumn(0).setMaxWidth(width * 5); |
| myVariableTable.setPreferredScrollableViewportSize(new Dimension(200, 130)); |
| |
| final List<Namespace> n = copy(namespaces); |
| myNamespaceTableModel = new NamespaceTableModel(n); |
| myNamespaceTable = new Table(myNamespaceTableModel); |
| myNamespaceTable.setDefaultRenderer(String.class, new NamespaceCellRenderer(n)); |
| |
| width = new JLabel("Prefix").getPreferredSize().width; |
| myNamespaceTable.getColumnModel().getColumn(0).setMinWidth(width); |
| myNamespaceTable.getColumnModel().getColumn(0).setMaxWidth(width * 4); |
| myNamespaceTable.setPreferredScrollableViewportSize(new Dimension(200, 150)); |
| |
| init(); |
| } |
| |
| protected JComponent createCenterPanel() { |
| final JPanel p = ToolbarDecorator.createDecorator(myVariableTable) |
| .setAddAction(new AnActionButtonRunnable() { |
| @Override |
| public void run(AnActionButton button) { |
| stopVarEditing(); |
| myVariableTableModel.addVariable(); |
| myNamespaceTable.editCellAt(myVariableTableModel.getRowCount() - 1, 0); |
| } |
| }).setRemoveAction(new AnActionButtonRunnable() { |
| @Override |
| public void run(AnActionButton button) { |
| myVariableTableModel.removeVariable(myVariableTable.getSelectedRow()); |
| } |
| }).disableUpDownActions().createPanel(); |
| UIUtil.addBorder(p, IdeBorderFactory.createTitledBorder("Variables", false)); |
| |
| final JPanel n = ToolbarDecorator.createDecorator(myNamespaceTable) |
| .setAddAction(myContextProvider.getContextElement() != null ? null : new AnActionButtonRunnable() { |
| @Override |
| public void run(AnActionButton button) { |
| final ExternalResourceManager erm = ExternalResourceManager.getInstance(); |
| final List<String> allURIs = new ArrayList<String>(Arrays.asList(erm.getResourceUrls(null, true))); |
| final Collection<Namespace> namespaces = myNamespaceTableModel.getNamespaces(); |
| for (Namespace namespace : namespaces) { |
| allURIs.remove(namespace.getUri()); |
| } |
| Collections.sort(allURIs); |
| |
| final DataContext dataContext = DataManager.getInstance().getDataContext(myNamespaceTable); |
| final Project project = CommonDataKeys.PROJECT.getData(dataContext); |
| final AddNamespaceDialog dlg = new AddNamespaceDialog(project, myUnresolvedPrefixes, allURIs, AddNamespaceDialog.Mode.EDITABLE); |
| dlg.show(); |
| if (dlg.isOK()) { |
| myNamespaceTableModel.addNamespace(new Namespace(dlg.getPrefix(), dlg.getURI())); |
| } |
| } |
| }).setRemoveAction( |
| myContextProvider.getContextElement() != null ? null : new AnActionButtonRunnable() { |
| @Override |
| public void run(AnActionButton button) { |
| myNamespaceTableModel.removeNamespace(myNamespaceTable.getSelectedRow()); |
| } |
| }).disableUpDownActions().createPanel(); |
| UIUtil.addBorder(n, IdeBorderFactory.createTitledBorder("Namespaces", false)); |
| |
| int extendedState = myDimensionService.getExtendedState(getDimensionServiceKey()); |
| if (extendedState == -1) extendedState = 400; |
| mySplitter = new Splitter(true, extendedState / 1000f); |
| mySplitter.setHonorComponentsMinimumSize(true); |
| mySplitter.setFirstComponent(n); |
| mySplitter.setSecondComponent(p); |
| |
| return mySplitter; |
| } |
| |
| |
| protected String getDimensionServiceKey() { |
| return getClass().getName() + ".DIMENSION_SERVICE_KEY"; |
| } |
| |
| public Pair<Collection<Namespace>, Collection<Variable>> getContext() { |
| assert isOK(); |
| |
| return Pair.create(myNamespaceTableModel.getNamespaces(), myVariableTableModel.getVariables()); |
| } |
| |
| protected void doOKAction() { |
| stopVarEditing(); |
| stopNamespaceEditing(); |
| |
| final List<Expression> expressions = myVariableTableModel.getExpressions(); |
| for (int i = 0; i < expressions.size(); i++) { |
| final Expression expression = expressions.get(i); |
| final String name = (String)myVariableTableModel.getValueAt(i, 0); |
| |
| final String expr = expression.getExpression(); |
| if ((expr == null || expr.trim().length() == 0) && (name == null || name.trim().length() == 0)) { |
| continue; |
| } |
| final String error = getError(expression); |
| if (error != null) { |
| Messages.showErrorDialog(expression.getFile().getProject(), "Error in XPath Expression for Variable '" + name + "': " + error, |
| "XPath Error"); |
| myVariableTable.getSelectionModel().setSelectionInterval(i, i); |
| return; |
| } |
| } |
| super.doOKAction(); |
| } |
| |
| public void show() { |
| super.show(); |
| if (mySplitter != null) { |
| myDimensionService.setExtendedState(getDimensionServiceKey(), (int)(mySplitter.getProportion() * 1000)); |
| } |
| } |
| |
| private String getError(final Expression expression) { |
| return MyPsiUtil.checkFile(expression.getFile()); |
| } |
| |
| private void stopVarEditing() { |
| final TableCellEditor cellEditor = myVariableTable.getCellEditor(); |
| if (cellEditor != null) { |
| cellEditor.stopCellEditing(); |
| } |
| } |
| |
| private void stopNamespaceEditing() { |
| final TableCellEditor cellEditor = myNamespaceTable.getCellEditor(); |
| if (cellEditor != null) { |
| cellEditor.stopCellEditing(); |
| } |
| } |
| |
| private class VariableTableModel extends AbstractTableModel { |
| private final Project myProject; |
| private final LanguageFileType myFileType; |
| |
| private final List<Variable> myVariables; |
| private final List<Expression> myList; |
| |
| public VariableTableModel(List<Variable> variables, Project project, LanguageFileType fileType) { |
| this.myVariables = variables; |
| this.myProject = project; |
| this.myFileType = fileType; |
| |
| myList = new ArrayList<Expression>(variables.size()); |
| for (Variable variable : variables) { |
| final Expression expression = Expression.create(project, fileType, variable.getExpression()); |
| myContextProvider.attachTo(expression.getFile()); |
| myList.add(expression); |
| } |
| } |
| |
| public Class<?> getColumnClass(int columnIndex) { |
| return columnIndex == 0 ? String.class : Expression.class; |
| } |
| |
| public boolean isCellEditable(int rowIndex, int columnIndex) { |
| return true; |
| } |
| |
| public String getColumnName(int column) { |
| return column == 0 ? "Name" : "Expression"; |
| } |
| |
| public int getRowCount() { |
| return myVariables.size(); |
| } |
| |
| public int getColumnCount() { |
| return 2; |
| } |
| |
| public Object getValueAt(int rowIndex, int columnIndex) { |
| final Variable variable = myVariables.get(rowIndex); |
| if (columnIndex == 0) { |
| return variable.getName(); |
| } |
| else { |
| return myList.get(rowIndex); |
| } |
| } |
| |
| public void setValueAt(Object aValue, int rowIndex, int columnIndex) { |
| if (rowIndex >= myVariables.size()) { |
| return; |
| } |
| final Variable variable = myVariables.get(rowIndex); |
| if (columnIndex == 0) { |
| variable.setName((String)aValue); |
| } |
| else { |
| variable.setExpression(((Expression)aValue).getExpression()); |
| } |
| fireTableDataChanged(); |
| } |
| |
| public Collection<Variable> getVariables() { |
| return myVariables; |
| } |
| |
| public List<Expression> getExpressions() { |
| return myList; |
| } |
| |
| public void addVariable() { |
| final Variable variable = new Variable(); |
| myVariables.add(variable); |
| final Expression expression = Expression.create(myProject, myFileType); |
| myContextProvider.attachTo(expression.getFile()); |
| myList.add(expression); |
| |
| final int firstRow = myVariables.size() - 1; |
| fireTableRowsInserted(firstRow, firstRow); |
| } |
| |
| public void removeVariable(int selectedRow) { |
| myVariables.remove(selectedRow); |
| myList.remove(selectedRow); |
| fireTableRowsDeleted(selectedRow, selectedRow); |
| } |
| } |
| |
| private static class VariableCellRenderer extends DefaultTableCellRenderer { |
| private final List<Variable> myVariables; |
| |
| public VariableCellRenderer(List<Variable> variables) { |
| this.myVariables = variables; |
| } |
| |
| public Component getTableCellRendererComponent(JTable table, Object _value, |
| boolean isSelected, boolean hasFocus, int row, int column) { |
| super.getTableCellRendererComponent(table, _value, isSelected, hasFocus, row, column); |
| setForeground(table.getForeground()); |
| setToolTipText(null); |
| |
| for (int i = 0; i < myVariables.size(); i++) { |
| Variable variable = myVariables.get(i); |
| if (i != row && variable.getName().equals(_value)) { |
| setForeground(Color.RED); |
| setToolTipText("Duplicate Variable"); |
| } |
| else if (variable.getExpression().length() == 0) { |
| setForeground(PlatformColors.BLUE); |
| setToolTipText("Empty expression. Variable will evaluate to empty nodeset."); |
| } |
| } |
| return this; |
| } |
| } |
| |
| private static class NamespaceTableModel extends AbstractTableModel { |
| private final List<Namespace> myNamespaces; |
| |
| public NamespaceTableModel(List<Namespace> namespaces) { |
| this.myNamespaces = namespaces; |
| } |
| |
| public Collection<Namespace> getNamespaces() { |
| return myNamespaces; |
| } |
| |
| public Class<?> getColumnClass(int columnIndex) { |
| return String.class; |
| } |
| |
| public boolean isCellEditable(int rowIndex, int columnIndex) { |
| return columnIndex == 0; |
| } |
| |
| public String getColumnName(int column) { |
| return column == 0 ? "Prefix" : "URI"; |
| } |
| |
| public int getRowCount() { |
| return myNamespaces.size(); |
| } |
| |
| public int getColumnCount() { |
| return 2; |
| } |
| |
| public Object getValueAt(int rowIndex, int columnIndex) { |
| final Namespace namespace = myNamespaces.get(rowIndex); |
| return columnIndex == 0 ? namespace.getPrefix() : namespace.getUri(); |
| } |
| |
| public void setValueAt(Object aValue, int rowIndex, int columnIndex) { |
| if (columnIndex == 0) { |
| final Namespace namespace = myNamespaces.get(rowIndex); |
| namespace.setPrefix((String)aValue); |
| } |
| } |
| |
| public void addNamespace(Namespace namespace) { |
| final int firstRow = myNamespaces.size(); |
| myNamespaces.add(namespace); |
| fireTableRowsInserted(firstRow, firstRow); |
| } |
| |
| public void removeNamespace(int selectedRow) { |
| myNamespaces.remove(selectedRow); |
| fireTableRowsDeleted(selectedRow, selectedRow); |
| } |
| } |
| |
| private static class NamespaceCellRenderer extends DefaultTableCellRenderer { |
| private final List<Namespace> myNamespaces; |
| |
| public NamespaceCellRenderer(List<Namespace> namespaces) { |
| this.myNamespaces = namespaces; |
| } |
| |
| public Component getTableCellRendererComponent(JTable table, Object value, |
| boolean isSelected, boolean hasFocus, int row, int column) { |
| super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); |
| setForeground(table.getForeground()); |
| |
| final String prefix = myNamespaces.get(row).getPrefix(); |
| if (column == 1 && prefix == null || prefix.length() == 0) { |
| setForeground(PlatformColors.BLUE); |
| } |
| else if (column == 0) { |
| for (int i = 0; i < myNamespaces.size(); i++) { |
| Namespace namespace = myNamespaces.get(i); |
| if (i != row && namespace.getPrefix().equals(value)) { |
| setForeground(Color.RED); |
| break; |
| } |
| } |
| } |
| return this; |
| } |
| } |
| |
| private class MyNamespaceContext implements NamespaceContext { |
| @Nullable |
| public String getNamespaceURI(String prefix, XmlElement context) { |
| return Namespace.makeMap(myNamespaceTableModel.getNamespaces()).get(prefix); |
| } |
| |
| @Nullable |
| public String getPrefixForURI(String uri, XmlElement context) { |
| final BidirectionalMap<String, String> bidiMap = new BidirectionalMap<String, String>(); |
| bidiMap.putAll(Namespace.makeMap(myNamespaceTableModel.getNamespaces())); |
| final List<String> list = bidiMap.getKeysByValue(uri); |
| return list != null && list.size() > 0 ? list.get(0) : null; |
| } |
| |
| @NotNull |
| public Collection<String> getKnownPrefixes(XmlElement context) { |
| return Namespace.makeMap(myNamespaceTableModel.getNamespaces()).keySet(); |
| } |
| |
| @Nullable |
| public PsiElement resolve(String prefix, XmlElement context) { |
| return null; |
| } |
| |
| public IntentionAction[] getUnresolvedNamespaceFixes(PsiReference reference, String localName) { |
| return IntentionAction.EMPTY_ARRAY; |
| } |
| |
| @Override |
| public String getDefaultNamespace(XmlElement context) { |
| return null; |
| } |
| } |
| |
| private class MyVariableContext extends SimpleVariableContext { |
| @NotNull |
| public String[] getVariablesInScope(XPathElement element) { |
| final Collection<Variable> variables = myVariableTableModel.getVariables(); |
| return Variable.asSet(variables).toArray(new String[variables.size()]); |
| } |
| } |
| |
| private class MyContextProvider extends ContextProvider { |
| private final MyNamespaceContext myNamespaceContext; |
| private final MyVariableContext myVariableContext; |
| private final ContextProvider myContextProvider; |
| |
| public MyContextProvider(ContextProvider contextProvider) { |
| myContextProvider = contextProvider; |
| myNamespaceContext = new MyNamespaceContext(); |
| myVariableContext = new MyVariableContext(); |
| } |
| |
| @NotNull |
| public ContextType getContextType() { |
| return myContextProvider.getContextType(); |
| } |
| |
| @Nullable |
| public XmlElement getContextElement() { |
| return myContextProvider.getContextElement(); |
| } |
| |
| @Nullable |
| public NamespaceContext getNamespaceContext() { |
| return myNamespaceContext; |
| } |
| |
| @Nullable |
| public VariableContext getVariableContext() { |
| return myVariableContext; |
| } |
| |
| @Nullable |
| public Set<QName> getAttributes(boolean forValidation) { |
| return myContextProvider.getAttributes(forValidation); |
| } |
| |
| @Nullable |
| public Set<QName> getElements(boolean forValidation) { |
| return myContextProvider.getElements(forValidation); |
| } |
| } |
| } |