/** * Copyright 2004-2016 Riccardo Solmi. All rights reserved. * This file is part of the Whole Platform. * * The Whole Platform is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The Whole Platform is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the Whole Platform. If not, see <http://www.gnu.org/licenses/>. */ package org.whole.lang.ui.util; import static org.whole.lang.commons.reflect.CommonsEntityDescriptorEnum.SameStageFragment; import static java.nio.file.StandardOpenOption.*; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.file.Files; import java.nio.file.Path; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.zip.CRC32; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPartFactory; import org.eclipse.gef.EditPartViewer; import org.eclipse.gef.RootEditPart; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.ImageLoader; import org.whole.lang.bindings.IBindingManager; import org.whole.lang.codebase.IPersistenceKit; import org.whole.lang.codebase.StringPersistenceProvider; import org.whole.lang.commons.factories.CommonsEntityFactory; import org.whole.lang.commons.model.RootFragment; import org.whole.lang.commons.reflect.CommonsEntityDescriptorEnum; import org.whole.lang.model.IEntity; import org.whole.lang.operations.PrettyPrinterOperation; import org.whole.lang.reflect.EntityDescriptor; import org.whole.lang.reflect.ReflectionFactory; import org.whole.lang.ui.actions.Clipboard; import org.whole.lang.ui.editor.IGEFEditorKit; import org.whole.lang.ui.editparts.IEntityPart; import org.whole.lang.ui.editparts.ITextualEntityPart; import org.whole.lang.ui.tools.Tools; import org.whole.lang.util.DataTypeUtils; import org.whole.lang.util.EntityUtils; /** * @author Enrico Persiani */ public class ClipboardUtils { public static final String DEFAULT_OUTPUT_FILENAME = "WholeSnapshot"; public static final int DEFAULT_OUTPUT_DPI = 300; public static String unparseEntity(IEntity entity) throws Exception { StringPersistenceProvider pp = new StringPersistenceProvider(); ReflectionFactory.getDefaultPersistenceKit().writeModel(entity, pp); return pp.getStore(); } public static IEntity parseEntity(String text) throws Exception { return ReflectionFactory.getDefaultPersistenceKit().readModel(new StringPersistenceProvider(text)); } public static IEntity parseClipboardContents(IPersistenceKit persistenceKit, IBindingManager bm) throws Exception { IEntity entity = Clipboard.instance().getEntityContents(); if (entity != null && ReflectionFactory.getDefaultPersistenceKit().equals(persistenceKit)) { if (EntityUtils.isTuple(entity)) bm.wDef("syntheticRoot", entity); return entity; } else { String text; if (entity != null) { StringBuilder sb = new StringBuilder(1024); for (int i=0; i<entity.wSize(); i++) sb.append(PrettyPrinterOperation.toPrettyPrintString(entity.wGet(i))); text = sb.toString(); } else text = Clipboard.instance().getTextContents(); if (text == null) throw new IllegalStateException("no clipboard contents"); bm.wDefValue("parseFragments", true); return persistenceKit.readModel(new StringPersistenceProvider(text, bm)); } } private static final SecureRandom SECURE_RANDOM = new SecureRandom(); private static Path wholeSnapshots; public static File createTempImageFile(String fileName, ImageData imageData, int dpi) throws IOException, FileNotFoundException { if (wholeSnapshots == null) { wholeSnapshots = Files.createTempDirectory("whole-snapshots"); wholeSnapshots.toFile().deleteOnExit(); } ImageLoader loader = new ImageLoader(); loader.data = new ImageData[] {imageData}; Path snapshotPath = wholeSnapshots.resolve(fileName+'-'+Long.toHexString(SECURE_RANDOM.nextLong())+".png"); OutputStream os = Files.newOutputStream(snapshotPath, CREATE, WRITE, TRUNCATE_EXISTING); loader.save(os, SWT.IMAGE_PNG); os.close(); File file = snapshotPath.toFile(); updatePngDpi(file, dpi); return file; } private static final String PNG_MAGIC = "\u0089PNG\r\n\u001a\n"; private static final String IHDR_CHUNK = "IHDR"; private static final String pHYs_CHUNK = "pHYs"; private static final int CHUNK_BASE_LENGTH = 12; // 4bytes (length) + 4bytes (signature) + 4bytes (crc) private static final int pHYs_DATA_LENGTH = 9; // 4bytes (pointsperunit) + 4bytes (pointsperunit) + 1bytes (unit) private static final int pHYs_METERS_UNIT = 1; private static final float INCH_2_M = 0.0254f; private static void updatePngDpi(File file, int dpi) { int dpm = Math.round(Float.valueOf(dpi) / INCH_2_M); try (RandomAccessFile pngFile = new RandomAccessFile(file, "rws")) { IllegalArgumentException exception = new IllegalArgumentException("malformed png file"); byte[] magic = new byte[8]; pngFile.readFully(magic); if (!Arrays.equals(magic, PNG_MAGIC.getBytes("latin1"))) throw exception; int ihdrLength = pngFile.readInt(); byte[] ihdr = new byte[4]; pngFile.readFully(ihdr); if (!Arrays.equals(ihdr, IHDR_CHUNK.getBytes("ascii"))) throw exception; byte[] ihdrData = new byte[ihdrLength]; pngFile.readFully(ihdrData); CRC32 crc32 = new CRC32(); crc32.update(ihdr); crc32.update(ihdrData); ByteBuffer byteBuffer = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN); byte[] ihdrCrc32 = new byte[4]; pngFile.readFully(ihdrCrc32); if (!Arrays.equals(ihdrCrc32, byteBuffer.putInt((int) crc32.getValue()).array())) throw exception; long offset = pngFile.getFilePointer(); long length = pngFile.length(); byte[] buffer = new byte[1024]; long fromOffset = length - buffer.length; long toOffset = fromOffset + CHUNK_BASE_LENGTH + pHYs_DATA_LENGTH; while(fromOffset >= offset) { pngFile.seek(fromOffset); pngFile.readFully(buffer); pngFile.seek(toOffset); pngFile.write(buffer); fromOffset -= buffer.length; toOffset -= buffer.length; } fromOffset += buffer.length; toOffset += buffer.length; if (fromOffset > offset) { pngFile.seek(offset); pngFile.readFully(buffer, 0, (int) (fromOffset - offset)); pngFile.seek(offset + CHUNK_BASE_LENGTH + pHYs_DATA_LENGTH); pngFile.write(buffer, 0, (int) (fromOffset - offset)); } // append pHYs after actual IHDR pngFile.seek(offset); pngFile.writeInt(pHYs_DATA_LENGTH); pngFile.write(buffer = pHYs_CHUNK.getBytes("ascii")); pngFile.writeInt(dpm); pngFile.writeInt(dpm); pngFile.write(pHYs_METERS_UNIT); byteBuffer.clear(); crc32.reset(); crc32.update(buffer); // pHYs crc32.update(buffer = byteBuffer.putInt(dpm).array()); crc32.update(buffer); crc32.update(pHYs_METERS_UNIT); pngFile.writeInt((int) crc32.getValue()); } catch (Exception e) { throw new IllegalArgumentException("cannot update png file"); } } public static File createTempXmlBuilderFile(IEntity entity) throws Exception { return createTempXmlBuilderFile(unparseEntity(entity)); } public static File createTempXmlBuilderFile(String contents) throws Exception { File file = File.createTempFile("whole-snippet", ".xwl"); OutputStream os = new FileOutputStream(file); os.write(contents.getBytes("UTF-8")); os.close(); return file; } public static boolean hasTextFocus(EditPartViewer viewer) { return hasTextFocus(viewer, viewer.getSelection()); } public static boolean hasTextFocus(EditPartViewer viewer, ISelectionProvider selectionProvider) { return hasTextFocus(viewer, selectionProvider.getSelection()); } public static boolean hasTextFocus(EditPartViewer viewer, ISelection selection) { List<EditPart> selectedParts = getSelectedEditParts(selection); if (selectedParts.size() > 1) return false; return Tools.TEXTUAL.isActive(viewer) && viewer.getFocusEditPart() instanceof ITextualEntityPart ; } public static boolean hasTextSeletion(EditPartViewer viewer) { return hasTextSeletion(viewer, viewer.getSelection()); } public static boolean hasTextSeletion(EditPartViewer viewer, ISelectionProvider selectionProvider) { return hasTextSeletion(viewer, selectionProvider.getSelection()); } public static boolean hasTextSeletion(EditPartViewer viewer, ISelection selection) { List<EditPart> selectedParts = getSelectedEditParts(selection); if (selectedParts.size() != 1) return false; EditPart selectedPart = selectedParts.get(0); return Tools.TEXTUAL.isActive(viewer) && selectedPart instanceof ITextualEntityPart && ((ITextualEntityPart) selectedPart).hasSelectionRange(); } public static String getTextSelection(EditPartViewer viewer) { return getTextSelection(viewer, viewer.getSelection()); } public static String getTextSelection(EditPartViewer viewer, ISelectionProvider selectionProvider) { return getTextSelection(viewer, selectionProvider.getSelection()); } public static String getTextSelection(EditPartViewer viewer, ISelection selection) { ITextualEntityPart textualEntityPart = (ITextualEntityPart) getSelectedEditParts(selection).get(0); return DataTypeUtils.getAsPresentationString(textualEntityPart.getModelEntity()).substring(textualEntityPart.getSelectionStart(), textualEntityPart.getSelectionEnd()); } public static List<EditPart> getSelectedEditParts(ISelectionProvider selectionProvider) { return getSelectedEditParts(selectionProvider.getSelection()); } @SuppressWarnings("unchecked") public static List<EditPart> getSelectedEditParts(ISelection selection) { if (!(selection instanceof IStructuredSelection)) return Collections.emptyList(); List<EditPart> selectedParts = new ArrayList<EditPart>(((IStructuredSelection) selection).toList()); // if there's a RootEditPart it must be the only element if (!selectedParts.isEmpty() && selectedParts.get(0).getParent() instanceof RootEditPart) selectedParts.remove(0); return selectedParts; } public static IEntityPart createEditPart(EditPartFactory factory, IEntity entity) { IGEFEditorKit commonsEditorKit = (IGEFEditorKit) ReflectionFactory.getEditorKit("org.whole.lang.commons.ui.CommonsEditorKit"); RootFragment rootFragment = CommonsEntityFactory.instance.createRootFragment( entity.wGetAdapter(CommonsEntityDescriptorEnum.Any)); EditPart rootFragmentPart = commonsEditorKit.getPartFactory().createEditPart(null, rootFragment); IEntityPart entityPart = (IEntityPart) factory.createEditPart(rootFragmentPart, entity); EntityUtils.remove(entity); return entityPart; } public static IEntity conditionalStageAdd(EditPart targetEditPart, EntityDescriptor<?> stage, IEntity entity, boolean needsCompositeTarget) { if (!(targetEditPart instanceof IEntityPart)) return entity; IEntity targetEntity = ((IEntityPart) targetEditPart).getModelEntity(); return conditionalStageAdd(targetEntity, stage, entity, needsCompositeTarget); } public static IEntity conditionalStageAdd(IEntity targetEntity, EntityDescriptor<?> stage, IEntity entity, boolean needsCompositeTarget) { assert !EntityUtils.hasParent(entity); EntityDescriptor<?> ed = entity.wGetEntityDescriptor(); if (SameStageFragment.equals(stage) && ((EntityUtils.isComposite(targetEntity) && EntityUtils.isAddable(targetEntity, ed)) || (!needsCompositeTarget && EntityUtils.isReplaceable(targetEntity, ed)))) return entity; return CommonsEntityFactory.instance.create(stage, entity); } }