/* * 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.Color; import java.awt.Component; import java.awt.Desktop; import java.awt.Dialog; import java.awt.Frame; import java.awt.Image; import java.awt.Toolkit; import java.awt.Window; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.awt.event.ActionEvent; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.GregorianCalendar; import java.util.List; import java.util.Properties; import javax.imageio.ImageIO; import javax.swing.AbstractAction; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JRootPane; import javax.swing.JTable; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.text.DefaultEditorKit; import javax.swing.undo.CannotUndoException; import javax.swing.undo.UndoManager; import org.apache.chemistry.opencmis.client.api.CmisObject; import org.apache.chemistry.opencmis.client.api.ObjectId; import org.apache.chemistry.opencmis.commons.data.ContentStream; import org.apache.chemistry.opencmis.commons.exceptions.CmisBaseException; import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException; import org.apache.chemistry.opencmis.commons.impl.IOUtils; import org.apache.chemistry.opencmis.workbench.swing.WorkbenchFileChooser; import org.apache.chemistry.opencmis.workbench.worker.OpenContentWorker; import org.apache.chemistry.opencmis.workbench.worker.StoreWorker; import org.apache.chemistry.opencmis.workbench.worker.TempFileContentWorker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class ClientHelper { public static final Color LINK_COLOR = new Color(105, 29, 21); public static final Color LINK_SELECTED_COLOR = new Color(255, 255, 255); public static final int TOOLBAR_ICON_SIZE = 20; public static final int BUTTON_ICON_SIZE = 11; public static final int OBJECT_ICON_SIZE = 16; public static final int ICON_BUTTON_ICON_SIZE = 16; public static final String UNDO_ACTION_KEY = "Undo"; public static final String REDO_ACTION_KEY = "Redo"; private static final Logger LOG = LoggerFactory.getLogger(ClientHelper.class); private static final ImageIcon CMIS_ICON = getIcon("icon256.png"); private static final List<BufferedImage> CMIS_ICON_LIST = new ArrayList<BufferedImage>(); static { try { CMIS_ICON_LIST.add(ImageIO.read(ClientHelper.class.getResource("/images/icon256.png"))); CMIS_ICON_LIST.add(ImageIO.read(ClientHelper.class.getResource("/images/icon128.png"))); CMIS_ICON_LIST.add(ImageIO.read(ClientHelper.class.getResource("/images/icon64.png"))); CMIS_ICON_LIST.add(ImageIO.read(ClientHelper.class.getResource("/images/icon48.png"))); CMIS_ICON_LIST.add(ImageIO.read(ClientHelper.class.getResource("/images/icon32.png"))); CMIS_ICON_LIST.add(ImageIO.read(ClientHelper.class.getResource("/images/icon16.png"))); } catch (Exception e) { LOG.error("Icons cannot be loaded!", e); } } private ClientHelper() { } public static void logError(Throwable t) { if (LOG.isErrorEnabled()) { LOG.error(t.getClass().getSimpleName() + ": " + t.getMessage(), t); if (t instanceof CmisBaseException) { CmisBaseException cex = (CmisBaseException) t; if (cex.getCode() != null) { LOG.error("Error code: " + cex.getCode()); } if (cex.getErrorContent() != null) { LOG.error("Error content: " + cex.getErrorContent()); } if (LOG.isDebugEnabled() && cex.getCause() != null) { LOG.debug("Cause: " + cex.getCause().toString(), cex.getCause()); } } } } public static void showError(Component parent, Throwable t) { logError(t); if (parent == null) { new ExceptionDialog((Frame) null, t); } else { Window window = (Window) SwingUtilities.getRoot(parent); if (window instanceof Frame) { new ExceptionDialog((Frame) window, t); } else if (window instanceof Dialog) { new ExceptionDialog((Dialog) window, t); } else { new ExceptionDialog((Frame) null, t); } } } public static boolean isMacOSX() { String osname = System.getProperty("os.name"); return osname == null ? false : osname.startsWith("Mac OS X"); } public static void installKeyBindings() { if (isMacOSX()) { final KeyStroke copyKeyStroke = KeyStroke.getKeyStroke("meta pressed C"); final KeyStroke pasteKeyStroke = KeyStroke.getKeyStroke("meta pressed V"); final KeyStroke cutKeyStroke = KeyStroke.getKeyStroke("meta pressed X"); final KeyStroke allKeyStroke = KeyStroke.getKeyStroke("meta pressed A"); InputMap textFieldMap = (InputMap) UIManager.get("TextField.focusInputMap"); textFieldMap.put(copyKeyStroke, DefaultEditorKit.copyAction); textFieldMap.put(pasteKeyStroke, DefaultEditorKit.pasteAction); textFieldMap.put(cutKeyStroke, DefaultEditorKit.cutAction); textFieldMap.put(allKeyStroke, DefaultEditorKit.selectAllAction); InputMap formattedTextFieldMap = (InputMap) UIManager.get("FormattedTextField.focusInputMap"); formattedTextFieldMap.put(copyKeyStroke, DefaultEditorKit.copyAction); formattedTextFieldMap.put(pasteKeyStroke, DefaultEditorKit.pasteAction); formattedTextFieldMap.put(cutKeyStroke, DefaultEditorKit.cutAction); formattedTextFieldMap.put(allKeyStroke, DefaultEditorKit.selectAllAction); InputMap textAreaMap = (InputMap) UIManager.get("TextArea.focusInputMap"); textAreaMap.put(copyKeyStroke, DefaultEditorKit.copyAction); textAreaMap.put(pasteKeyStroke, DefaultEditorKit.pasteAction); textAreaMap.put(cutKeyStroke, DefaultEditorKit.cutAction); textAreaMap.put(allKeyStroke, DefaultEditorKit.selectAllAction); InputMap editorPaneMap = (InputMap) UIManager.get("EditorPane.focusInputMap"); editorPaneMap.put(copyKeyStroke, DefaultEditorKit.copyAction); editorPaneMap.put(pasteKeyStroke, DefaultEditorKit.pasteAction); editorPaneMap.put(cutKeyStroke, DefaultEditorKit.cutAction); editorPaneMap.put(allKeyStroke, DefaultEditorKit.selectAllAction); InputMap passwordFieldMap = (InputMap) UIManager.get("PasswordField.focusInputMap"); passwordFieldMap.put(pasteKeyStroke, DefaultEditorKit.pasteAction); } } public static void installEscapeBinding(final Window window, final JRootPane rootPane, final boolean dispose) { final KeyStroke stroke = KeyStroke.getKeyStroke("ESCAPE"); final InputMap inputMap = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); inputMap.put(stroke, "ESCAPE"); rootPane.getActionMap().put("ESCAPE", new AbstractAction() { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { if (dispose) { window.dispose(); } else { window.setVisible(false); } } }); } public static AbstractAction createAndAttachUndoAction(final UndoManager undoManager, JComponent component) { AbstractAction undoAction = new AbstractAction(UNDO_ACTION_KEY) { private static final long serialVersionUID = 1L; public void actionPerformed(ActionEvent evt) { try { if (undoManager.canUndo()) { undoManager.undo(); } } catch (CannotUndoException e) { } } }; component.getActionMap().put(UNDO_ACTION_KEY, undoAction); KeyStroke undoKey = isMacOSX() ? KeyStroke.getKeyStroke("meta pressed Z") : KeyStroke.getKeyStroke("control pressed Z"); component.getInputMap().put(undoKey, UNDO_ACTION_KEY); return undoAction; } public static AbstractAction createAndAttachRedoAction(final UndoManager undoManager, JComponent component) { AbstractAction redoAction = new AbstractAction(REDO_ACTION_KEY) { private static final long serialVersionUID = 1L; public void actionPerformed(ActionEvent evt) { try { if (undoManager.canRedo()) { undoManager.redo(); } } catch (CannotUndoException e) { } } }; component.getActionMap().put(REDO_ACTION_KEY, redoAction); KeyStroke redoKey = isMacOSX() ? KeyStroke.getKeyStroke("meta shift pressed Z") : KeyStroke.getKeyStroke("control shift pressed Z"); component.getInputMap().put(redoKey, REDO_ACTION_KEY); return redoAction; } public static ImageIcon getIcon(String name) { URL imageURL = ClientHelper.class.getResource("/images/" + name); if (imageURL != null) { return WorkbenchScale.scaleIcon(new ImageIcon(imageURL)); } return null; } public static ImageIcon getCmisIconImage() { return CMIS_ICON; } public static List<? extends Image> getCmisIconImages() { return CMIS_ICON_LIST; } public static String getDateString(GregorianCalendar cal) { if (cal == null) { return ""; } SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ZZZ"); sdf.setTimeZone(cal.getTimeZone()); return sdf.format(cal.getTime()); } public static void download(Component component, CmisObject object, String streamId) { OpenContentWorker worker = new OpenContentWorker(null, object, streamId) { @Override protected void processStream(Component comp, ContentStream contentStream, String filename) { WorkbenchFileChooser fileChooser = new WorkbenchFileChooser(); fileChooser.setSelectedFile(new File(filename)); int chooseResult = fileChooser.showDialog(comp, "Download"); if (chooseResult == WorkbenchFileChooser.APPROVE_OPTION) { (new StoreWorker(contentStream, fileChooser.getSelectedFile(), filename)).executeTask(); } else { IOUtils.closeQuietly(contentStream); } } }; worker.executeTask(); } public static void open(Component component, CmisObject object, String streamId) { if (!Desktop.isDesktopSupported()) { download(component, object, streamId); return; } final Desktop desktop = Desktop.getDesktop(); if (!desktop.isSupported(Desktop.Action.OPEN)) { download(component, object, streamId); return; } TempFileContentWorker worker = new TempFileContentWorker(component, object, streamId) { @Override protected void processTempFile(File file) { try { desktop.open(file); } catch (Exception e) { if (e instanceof IOException) { copy(file); } else { showError(e); } } } private void copy(File file) { WorkbenchFileChooser fileChooser = new WorkbenchFileChooser(); fileChooser.setSelectedFile(new File(file.getName())); int chooseResult = fileChooser.showDialog(getComponent(), "Download"); if (chooseResult == WorkbenchFileChooser.APPROVE_OPTION && !file.equals(fileChooser.getSelectedFile())) { try { InputStream in = null; OutputStream out = null; try { in = new FileInputStream(file); out = new FileOutputStream(fileChooser.getSelectedFile()); IOUtils.copy(in, out, 64 * 1024); } finally { IOUtils.closeQuietly(in); IOUtils.closeQuietly(out); } } catch (Exception e) { showError(e); } } } }; worker.executeTask(); } public static File createTempFile(String filename) { String tempDir = System.getProperty("java.io.tmpdir"); File clientTempDir = new File(tempDir, "cmisworkbench"); if (!clientTempDir.exists() && !clientTempDir.mkdirs()) { throw new CmisRuntimeException("Could not create directory for temp file!"); } clientTempDir.deleteOnExit(); File tempFile = new File(clientTempDir, filename); tempFile.deleteOnExit(); return tempFile; } public static void copyTableToClipboard(JTable table, boolean onlySelected) { final String newline = System.getProperty("line.separator"); final StringBuilder sb = new StringBuilder(1024); final int rows = table.getModel().getRowCount(); final int cols = table.getModel().getColumnCount(); for (int col = 0; col < cols; col++) { if (col > 0) { sb.append(','); } sb.append(formatCSVValue(table.getModel().getColumnName(col))); } sb.append(newline); int[] seletedRows = table.getSelectedRows(); Arrays.sort(seletedRows); for (int row = 0; row < rows; row++) { if (onlySelected) { if (Arrays.binarySearch(seletedRows, row) < 0) { continue; } } for (int col = 0; col < cols; col++) { if (col > 0) { sb.append(','); } Object value = table.getModel().getValueAt(row, col); sb.append(formatCSVValue(value)); } sb.append(newline); } Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); Transferable transferable = new StringSelection(sb.toString()); clipboard.setContents(transferable, null); } public static String encodeHtml(StringBuilder sb, String s) { if (s == null) { return ""; } for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == '<') { sb.append("<"); } else if (c == '>') { sb.append(">"); } else if (c == '"') { sb.append("""); } else if (c == '\'') { sb.append("'"); } else if (c > 127) { sb.append("&#" + (int) c + ";"); } else { sb.append(c); } } return sb.toString(); } private static String formatCSVValue(Object value) { if (value == null) { return ""; } else if (value instanceof GregorianCalendar) { return getDateString((GregorianCalendar) value); } else if (value instanceof String) { String s = value.toString(); StringBuilder sb = new StringBuilder(s.length() + 16); sb.append('\"'); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); sb.append(c); if (c == '\"') { sb.append('\"'); } } sb.append('\"'); return sb.toString(); } else if (value instanceof Collection<?>) { StringBuilder sb = new StringBuilder(((Collection<?>) value).size() * 16 + 16); sb.append('['); for (Object v : (Collection<?>) value) { if (sb.length() > 1) { sb.append(','); } sb.append(formatCSVValue(v)); } sb.append(']'); return sb.toString(); } else if (value instanceof ObjectId) { return formatCSVValue(((ObjectId) value).getId()); } else if (value instanceof Icon) { return "<icon>"; } return value.toString(); } public static URI getClasspathURI(String path) { try { return ClientHelper.class.getResource(path).toURI(); } catch (URISyntaxException e) { // not very likely LOG.error(e.getMessage(), e); return null; } } public static String readFileAndRemoveHeader(final URI file) { if (file == null) { return ""; } final InputStream stream; try { stream = file.toURL().openStream(); } catch (Exception e) { return ""; } final String result = readStreamAndRemoveHeader(stream); IOUtils.closeQuietly(stream); return result; } public static String readStreamAndRemoveHeader(final InputStream stream) { if (stream == null) { return ""; } try { return IOUtils.readAllLinesAndRemoveHeader(stream, 10000); } catch (IOException e1) { return ""; } } public static List<FileEntry> readFileProperties(URI propertiesFile) { final InputStream stream; try { stream = propertiesFile.toURL().openStream(); if (stream == null) { return null; } } catch (Exception e) { LOG.error("Cannot open library file: {}", propertiesFile, e); return null; } String classpathParent = null; if ("classpath".equalsIgnoreCase(propertiesFile.getScheme())) { String path = propertiesFile.getSchemeSpecificPart(); int x = path.lastIndexOf('/'); if (x > -1) { classpathParent = path.substring(0, x); } } if ("jar".equalsIgnoreCase(propertiesFile.getScheme())) { String path = propertiesFile.getSchemeSpecificPart(); int x = path.lastIndexOf('/'); if (x > -1) { path = path.substring(0, x); x = path.indexOf("!/"); if (x > -1) { classpathParent = path.substring(x + 1); } } } String fileParent = null; if ("file".equalsIgnoreCase(propertiesFile.getScheme())) { fileParent = (new File(propertiesFile)).getParent(); } try { Properties properties = new Properties(); properties.load(stream); stream.close(); final List<FileEntry> result = new ArrayList<FileEntry>(); for (String file : properties.stringPropertyNames()) { try { URI uri = null; if (classpathParent != null) { URL url = ClientHelper.class.getResource(classpathParent + "/" + file); if (url != null) { uri = url.toURI(); } } if (fileParent != null) { uri = (new File(fileParent, file)).toURI(); } if (uri != null) { result.add(new FileEntry(properties.getProperty(file), uri)); } else { LOG.error("Cannot find library entry: {}", file); } } catch (URISyntaxException e) { // ignore entry } } Collections.sort(result); return result; } catch (IOException e) { LOG.error("Cannot read library file: {}", propertiesFile); return null; } finally { IOUtils.closeQuietly(stream); } } public static class FileEntry implements Comparable<FileEntry> { private final String name; private final URI file; public FileEntry(String name, URI file) { this.name = name; this.file = file; } public String getName() { return name; } public URI getFile() { return file; } @Override public String toString() { return name; } @Override public int compareTo(FileEntry o) { return name.compareToIgnoreCase(o.getName()); } @Override public int hashCode() { return name.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof FileEntry)) { return false; } return name.equals(((FileEntry) obj).getName()); } } }