| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.chemistry.opencmis.workbench; |
| |
| import java.awt.BorderLayout; |
| import java.awt.Component; |
| import java.awt.Cursor; |
| import java.awt.Dimension; |
| import java.awt.Point; |
| import java.awt.Toolkit; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.awt.event.MouseAdapter; |
| import java.awt.event.MouseEvent; |
| import java.awt.event.MouseMotionListener; |
| import java.util.GregorianCalendar; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.swing.BoxLayout; |
| import javax.swing.JButton; |
| import javax.swing.JCheckBox; |
| import javax.swing.JFormattedTextField; |
| import javax.swing.JFrame; |
| import javax.swing.JLabel; |
| import javax.swing.JMenuItem; |
| import javax.swing.JPanel; |
| import javax.swing.JPopupMenu; |
| import javax.swing.JScrollPane; |
| import javax.swing.JSplitPane; |
| import javax.swing.JTable; |
| import javax.swing.JTextArea; |
| import javax.swing.SwingConstants; |
| import javax.swing.WindowConstants; |
| import javax.swing.table.AbstractTableModel; |
| import javax.swing.table.TableCellRenderer; |
| import javax.swing.table.TableColumn; |
| import javax.swing.text.NumberFormatter; |
| |
| import org.apache.chemistry.opencmis.client.api.ItemIterable; |
| import org.apache.chemistry.opencmis.client.api.ObjectId; |
| import org.apache.chemistry.opencmis.client.api.QueryResult; |
| import org.apache.chemistry.opencmis.client.runtime.ObjectIdImpl; |
| import org.apache.chemistry.opencmis.commons.PropertyIds; |
| import org.apache.chemistry.opencmis.commons.data.PropertyData; |
| import org.apache.chemistry.opencmis.workbench.icons.CopyIcon; |
| import org.apache.chemistry.opencmis.workbench.icons.QueryIcon; |
| import org.apache.chemistry.opencmis.workbench.model.ClientModel; |
| import org.apache.chemistry.opencmis.workbench.swing.IdRenderer; |
| |
| public class QueryFrame extends JFrame { |
| |
| private static final long serialVersionUID = 1L; |
| |
| private static final Cursor HAND_CURSOR = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); |
| private static final Cursor DEFAULT_CURSOR = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR); |
| |
| private static final String WINDOW_TITLE = "CMIS Query"; |
| private static final String DEFAULT_QUERY = "SELECT * FROM cmis:document"; |
| |
| private static final String[] QUERY_SNIPPETS = new String[] { // |
| "SELECT * FROM cmis:document", // |
| "SELECT * FROM cmis:folder", // |
| "SELECT cmis:objectId, cmis:name, SCORE() AS score FROM cmis:document WHERE CONTAINS('?')", // |
| "WHERE cmis:name LIKE '%'", // |
| "WHERE ? IN (?, ?, ?)", // |
| "WHERE IN_FOLDER('?')", // |
| "WHERE IN_TREE('?')", // |
| "WHERE ? = TIMESTAMP 'YYYY-MM-DDThh:mm:ss.sss[Z|+hh:mm|-hh:mm]'", // |
| "WHERE '?' = ANY ?", // |
| "ORDER BY cmis:name", // |
| "ORDER BY cmis:creationDate" }; |
| |
| private final ClientModel model; |
| |
| private JTextArea queryText; |
| private JFormattedTextField skipCountField; |
| private JFormattedTextField maxHitsField; |
| private JCheckBox searchAllVersionsCheckBox; |
| private ResultTable resultsTable; |
| private JLabel queryTimeLabel; |
| |
| public QueryFrame(ClientModel model) { |
| super(); |
| |
| this.model = model; |
| createGUI(); |
| } |
| |
| private void createGUI() { |
| setTitle(WINDOW_TITLE + " - " + model.getRepositoryName()); |
| setIconImages(ClientHelper.getCmisIconImages()); |
| |
| Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); |
| setPreferredSize(new Dimension((int) (screenSize.getWidth() / 2), (int) (screenSize.getHeight() / 2))); |
| setMinimumSize(new Dimension(200, 60)); |
| |
| setLayout(new BoxLayout(getContentPane(), BoxLayout.PAGE_AXIS)); |
| |
| // input |
| JPanel inputPanel = new JPanel(); |
| inputPanel.setLayout(new BoxLayout(inputPanel, BoxLayout.PAGE_AXIS)); |
| |
| // query text area |
| queryText = new JTextArea(DEFAULT_QUERY, 5, 60); |
| queryText.setLineWrap(true); |
| queryText.setPreferredSize(new Dimension(Short.MAX_VALUE, queryText.getPreferredSize().height)); |
| inputPanel.add(queryText); |
| |
| JPanel inputPanel2 = new JPanel(); |
| inputPanel2.setLayout(new BorderLayout()); |
| |
| // buttons |
| JPanel buttonPanel = new JPanel(); |
| |
| skipCountField = new JFormattedTextField(new NumberFormatter()); |
| skipCountField.setValue(Integer.valueOf(0)); |
| skipCountField.setColumns(5); |
| |
| JLabel skipCountLabel = new JLabel("Skip:"); |
| skipCountLabel.setLabelFor(skipCountField); |
| |
| buttonPanel.add(skipCountLabel); |
| buttonPanel.add(skipCountField); |
| |
| maxHitsField = new JFormattedTextField(new NumberFormatter()); |
| maxHitsField.setValue(Integer.valueOf(100)); |
| maxHitsField.setColumns(5); |
| |
| JLabel maxHitsLabel = new JLabel("Max hits:"); |
| maxHitsLabel.setLabelFor(maxHitsField); |
| |
| buttonPanel.add(maxHitsLabel); |
| buttonPanel.add(maxHitsField); |
| |
| searchAllVersionsCheckBox = new JCheckBox("search all versions", false); |
| buttonPanel.add(searchAllVersionsCheckBox); |
| |
| JButton queryButton = new JButton("Query", new QueryIcon(ClientHelper.BUTTON_ICON_SIZE, |
| ClientHelper.BUTTON_ICON_SIZE)); |
| queryButton.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| doQuery(); |
| } |
| }); |
| |
| buttonPanel.add(queryButton); |
| |
| inputPanel2.add(buttonPanel, BorderLayout.LINE_END); |
| |
| // snippets |
| final JPopupMenu snippetsPopup = new JPopupMenu(); |
| for (final String s : QUERY_SNIPPETS) { |
| JMenuItem menuItem = new JMenuItem(s); |
| menuItem.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| queryText.insert(s, queryText.getCaretPosition()); |
| } |
| }); |
| snippetsPopup.add(menuItem); |
| } |
| |
| final JButton snippetButton = new JButton("Query Snippets", new CopyIcon(20, 20)); |
| snippetButton.setFocusPainted(true); |
| snippetButton.setBorderPainted(false); |
| snippetButton.setContentAreaFilled(false); |
| snippetButton.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| snippetsPopup.show(snippetButton, 0, snippetButton.getHeight()); |
| } |
| }); |
| |
| inputPanel2.add(snippetButton, BorderLayout.LINE_START); |
| |
| // query time label |
| queryTimeLabel = new JLabel(""); |
| queryTimeLabel.setHorizontalAlignment(SwingConstants.CENTER); |
| inputPanel2.add(queryTimeLabel, BorderLayout.CENTER); |
| |
| inputPanel2.setMaximumSize(new Dimension(Short.MAX_VALUE, inputPanel2.getPreferredSize().height)); |
| inputPanel.add(inputPanel2); |
| |
| // table |
| resultsTable = new ResultTable(); |
| |
| final JPopupMenu popup = new JPopupMenu(); |
| final JMenuItem clipboardAllItem = new JMenuItem("Copy all rows to clipboard"); |
| popup.add(clipboardAllItem); |
| |
| clipboardAllItem.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| ClientHelper.copyTableToClipboard(resultsTable, false); |
| } |
| }); |
| |
| final JMenuItem clipboardSelectedItem = new JMenuItem("Copy selected rows to clipboard"); |
| popup.add(clipboardSelectedItem); |
| |
| clipboardSelectedItem.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| ClientHelper.copyTableToClipboard(resultsTable, true); |
| } |
| }); |
| |
| resultsTable.addMouseListener(new MouseAdapter() { |
| @Override |
| public void mouseClicked(MouseEvent e) { |
| int row = resultsTable.rowAtPoint(e.getPoint()); |
| int column = resultsTable.columnAtPoint(e.getPoint()); |
| if (row > -1 && resultsTable.getColumnClass(column) == ObjectIdImpl.class) { |
| try { |
| model.loadObject(((ObjectId) resultsTable.getValueAt(row, column)).getId()); |
| } catch (Exception ex) { |
| ClientHelper.showError(QueryFrame.this, ex); |
| } |
| } |
| } |
| |
| @Override |
| public void mousePressed(MouseEvent e) { |
| maybeShowPopup(e); |
| } |
| |
| @Override |
| public void mouseReleased(MouseEvent e) { |
| maybeShowPopup(e); |
| } |
| |
| private void maybeShowPopup(MouseEvent e) { |
| if (e.isPopupTrigger()) { |
| popup.show(e.getComponent(), e.getX(), e.getY()); |
| } |
| } |
| }); |
| |
| resultsTable.addMouseMotionListener(new MouseMotionListener() { |
| @Override |
| public void mouseMoved(MouseEvent e) { |
| int row = resultsTable.rowAtPoint(e.getPoint()); |
| int column = resultsTable.columnAtPoint(e.getPoint()); |
| if (row > -1 && resultsTable.getColumnClass(column) == ObjectIdImpl.class) { |
| resultsTable.setCursor(HAND_CURSOR); |
| } else { |
| resultsTable.setCursor(DEFAULT_CURSOR); |
| } |
| } |
| |
| @Override |
| public void mouseDragged(MouseEvent e) { |
| } |
| }); |
| |
| add(new JSplitPane(JSplitPane.VERTICAL_SPLIT, inputPanel, new JScrollPane(resultsTable))); |
| |
| getRootPane().setDefaultButton(queryButton); |
| |
| ClientHelper.installEscapeBinding(this, getRootPane(), true); |
| |
| setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); |
| pack(); |
| |
| setLocationRelativeTo(null); |
| setVisible(true); |
| } |
| |
| private synchronized void doQuery() { |
| String text = queryText.getText(); |
| text = text.replace('\n', ' '); |
| |
| ItemIterable<QueryResult> results = null; |
| |
| try { |
| setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); |
| |
| int skipCount = 0; |
| try { |
| skipCountField.commitEdit(); |
| skipCount = ((Number) skipCountField.getValue()).intValue(); |
| if (skipCount < 0) { |
| skipCount = 0; |
| skipCountField.setValue(0); |
| } |
| } catch (Exception e) { |
| ClientHelper.showError(this, e); |
| } |
| |
| int maxHits = 1000; |
| try { |
| maxHitsField.commitEdit(); |
| maxHits = ((Number) maxHitsField.getValue()).intValue(); |
| if (maxHits < 0) { |
| maxHits = 0; |
| maxHitsField.setValue(0); |
| } |
| } catch (Exception e) { |
| ClientHelper.showError(this, e); |
| } |
| |
| results = model.query(text, searchAllVersionsCheckBox.isSelected(), maxHits); |
| |
| ResultTableModel rtm = new ResultTableModel(); |
| |
| long startTime = System.currentTimeMillis(); |
| |
| int row = 0; |
| ItemIterable<QueryResult> page = results.getPage(maxHits); |
| if (skipCount > 0) { |
| page = page.skipTo(skipCount); |
| } |
| for (QueryResult qr : page) { |
| rtm.setColumnCount(Math.max(rtm.getColumnCount(), qr.getProperties().size())); |
| |
| for (PropertyData<?> prop : qr.getProperties()) { |
| if (PropertyIds.OBJECT_ID.equals(prop.getId()) && (prop.getFirstValue() != null)) { |
| rtm.setValue(row, prop.getQueryName(), new ObjectIdImpl(prop.getFirstValue().toString())); |
| } else { |
| rtm.setValue(row, prop.getQueryName(), prop.getValues()); |
| } |
| } |
| |
| row++; |
| } |
| rtm.setRowCount(row); |
| |
| long stopTime = System.currentTimeMillis(); |
| float time = (stopTime - startTime) / 1000f; |
| String total = "<unknown>"; |
| if (page.getTotalNumItems() >= 0) { |
| total = String.valueOf(page.getTotalNumItems()); |
| } |
| |
| queryTimeLabel.setText(" " + row + " hits, " + total + " total (" + time + " sec)"); |
| |
| resultsTable.setModel(rtm); |
| } catch (Exception ex) { |
| ClientHelper.showError(null, ex); |
| return; |
| } finally { |
| setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); |
| } |
| } |
| |
| static class ResultTableModel extends AbstractTableModel { |
| |
| private static final long serialVersionUID = 1L; |
| |
| private int columnCount = 0; |
| private int rowCount = 0; |
| private final Map<String, Integer> columnMapping = new HashMap<String, Integer>(); |
| private final Map<Integer, Map<Integer, Object>> data = new HashMap<Integer, Map<Integer, Object>>(); |
| private final Map<Integer, Map<Integer, List<?>>> multivalue = new HashMap<Integer, Map<Integer, List<?>>>(); |
| private final Map<Integer, Class<?>> columnClass = new HashMap<Integer, Class<?>>(); |
| |
| public ResultTableModel() { |
| } |
| |
| public void setColumnCount(int columnCount) { |
| this.columnCount = columnCount; |
| } |
| |
| @Override |
| public int getColumnCount() { |
| return columnCount; |
| } |
| |
| public void setRowCount(int rowCount) { |
| this.rowCount = rowCount; |
| } |
| |
| @Override |
| public int getRowCount() { |
| return rowCount; |
| } |
| |
| public void setValue(final int rowIndex, final String queryName, Object value) { |
| Integer col = columnMapping.get(queryName); |
| if (col == null) { |
| col = columnMapping.size(); |
| columnMapping.put(queryName, columnMapping.size()); |
| } |
| |
| if (value == null) { |
| return; |
| } |
| |
| if (value instanceof List<?>) { |
| List<?> values = (List<?>) value; |
| if (values.isEmpty()) { |
| return; |
| } |
| |
| value = values.get(0); |
| |
| if (values.size() > 1) { |
| Map<Integer, List<?>> mvrow = multivalue.get(rowIndex); |
| if (mvrow == null) { |
| mvrow = new HashMap<Integer, List<?>>(); |
| multivalue.put(rowIndex, mvrow); |
| } |
| mvrow.put(col, values); |
| } |
| } |
| |
| if (value instanceof GregorianCalendar) { |
| value = ClientHelper.getDateString((GregorianCalendar) value); |
| } |
| |
| columnClass.put(col, value.getClass()); |
| |
| Map<Integer, Object> row = data.get(rowIndex); |
| if (row == null) { |
| row = new HashMap<Integer, Object>(); |
| data.put(rowIndex, row); |
| } |
| |
| row.put(col, value); |
| } |
| |
| @Override |
| public Object getValueAt(int rowIndex, int columnIndex) { |
| Map<Integer, Object> row = data.get(rowIndex); |
| if (row == null) { |
| return null; |
| } |
| |
| return row.get(columnIndex); |
| } |
| |
| public List<?> getMultiValueAt(int rowIndex, int columnIndex) { |
| Map<Integer, List<?>> row = multivalue.get(rowIndex); |
| if (row == null) { |
| return null; |
| } |
| |
| return row.get(columnIndex); |
| } |
| |
| @Override |
| public String getColumnName(int column) { |
| for (Map.Entry<String, Integer> e : columnMapping.entrySet()) { |
| if (e.getValue().equals(column)) { |
| return e.getKey(); |
| } |
| } |
| |
| return "?"; |
| } |
| |
| @Override |
| public Class<?> getColumnClass(int columnIndex) { |
| Class<?> clazz = columnClass.get(columnIndex); |
| if (clazz != null) { |
| return clazz; |
| } |
| |
| return String.class; |
| } |
| } |
| |
| static class ResultTable extends JTable { |
| |
| private static final long serialVersionUID = 1L; |
| |
| public ResultTable() { |
| super(); |
| |
| setDefaultRenderer(ObjectIdImpl.class, new IdRenderer()); |
| setFillsViewportHeight(true); |
| setAutoResizeMode(JTable.AUTO_RESIZE_OFF); |
| |
| setRowHeight((int) (getFontMetrics(getFont()).getHeight() * 1.1)); |
| } |
| |
| @Override |
| public String getToolTipText(final MouseEvent e) { |
| String result = null; |
| |
| Point p = e.getPoint(); |
| int rowIndex = rowAtPoint(p); |
| int columnIndex = convertColumnIndexToModel(columnAtPoint(p)); |
| |
| final ResultTableModel model = (ResultTableModel) getModel(); |
| |
| final List<?> values = model.getMultiValueAt(rowIndex, columnIndex); |
| if (values != null) { |
| StringBuilder sb = new StringBuilder(128); |
| |
| for (Object value : values) { |
| if (sb.length() == 0) { |
| sb.append("<html>"); |
| } else { |
| sb.append("<br>"); |
| } |
| if (value == null) { |
| sb.append("<i>null</i>"); |
| } else { |
| ClientHelper.encodeHtml(sb, value.toString()); |
| } |
| } |
| |
| result = sb.toString(); |
| } else { |
| final Object value = model.getValueAt(rowIndex, columnIndex); |
| if (value != null) { |
| result = value.toString(); |
| } |
| } |
| |
| return result; |
| } |
| |
| @Override |
| public Component prepareRenderer(final TableCellRenderer renderer, final int rowIndex, final int columnIndex) { |
| final Component prepareRenderer = super.prepareRenderer(renderer, rowIndex, columnIndex); |
| final TableColumn column = getColumnModel().getColumn(columnIndex); |
| |
| final int currentWidth = column.getPreferredWidth(); |
| if (currentWidth < 200) { |
| int width = prepareRenderer.getPreferredSize().width; |
| if (currentWidth < width) { |
| if (width < 50) { |
| width = 50; |
| } else if (width > 200) { |
| width = 200; |
| } |
| |
| if (width != currentWidth) { |
| column.setPreferredWidth(width); |
| } |
| } |
| } |
| |
| return prepareRenderer; |
| } |
| } |
| } |