| /******************************************************************************* |
| * Copyright (c) 2000, 2016 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * James Blackburn (Broadcom Corp.) - ongoing development |
| * Lars Vogel <Lars.Vogel@vogella.com> - Bug 473427 |
| * Mickael Istria (Red Hat Inc.) - Bug 488937 |
| *******************************************************************************/ |
| package org.eclipse.core.internal.resources; |
| |
| import java.io.DataOutputStream; |
| import java.io.IOException; |
| import java.util.*; |
| import org.eclipse.core.internal.watson.IPathRequestor; |
| |
| // |
| public class MarkerWriter { |
| |
| protected MarkerManager manager; |
| |
| // version numbers |
| public static final int MARKERS_SAVE_VERSION = 3; |
| public static final int MARKERS_SNAP_VERSION = 2; |
| |
| // type constants |
| public static final byte INDEX = 1; |
| public static final byte QNAME = 2; |
| |
| // marker attribute types |
| public static final byte ATTRIBUTE_NULL = 0; |
| public static final byte ATTRIBUTE_BOOLEAN = 1; |
| public static final byte ATTRIBUTE_INTEGER = 2; |
| public static final byte ATTRIBUTE_STRING = 3; |
| |
| public MarkerWriter(MarkerManager manager) { |
| super(); |
| this.manager = manager; |
| } |
| |
| /** |
| * Returns an Object array of length 2. The first element is an Integer which is the number |
| * of persistent markers found. The second element is an array of boolean values, with a |
| * value of true meaning that the marker at that index is to be persisted. |
| */ |
| private Object[] filterMarkers(IMarkerSetElement[] markers) { |
| Object[] result = new Object[2]; |
| boolean[] isPersistent = new boolean[markers.length]; |
| int count = 0; |
| for (int i = 0; i < markers.length; i++) { |
| MarkerInfo info = (MarkerInfo) markers[i]; |
| if (manager.isPersistent(info)) { |
| isPersistent[i] = true; |
| count++; |
| } |
| } |
| result[0] = count; |
| result[1] = isPersistent; |
| return result; |
| } |
| |
| /** |
| * <pre> {@code |
| * SAVE_FILE -> VERSION_ID RESOURCE+ |
| * VERSION_ID -> int |
| * RESOURCE -> RESOURCE_PATH MARKERS_SIZE MARKER+ |
| * RESOURCE_PATH -> String |
| * MARKERS_SIZE -> int |
| * MARKER -> MARKER_ID TYPE ATTRIBUTES_SIZE ATTRIBUTE* CREATION_TIME |
| * MARKER_ID -> long |
| * TYPE -> INDEX | QNAME |
| * INDEX -> byte int |
| * QNAME -> byte String |
| * ATTRIBUTES_SIZE -> short |
| * ATTRIBUTE -> ATTRIBUTE_KEY ATTRIBUTE_VALUE |
| * ATTRIBUTE_KEY -> String |
| * ATTRIBUTE_VALUE -> INTEGER_VALUE | BOOLEAN_VALUE | STRING_VALUE | NULL_VALUE |
| * INTEGER_VALUE -> byte int |
| * BOOLEAN_VALUE -> byte boolean |
| * STRING_VALUE -> byte String |
| * NULL_VALUE -> byte |
| * CREATION_TIME -> long |
| * }</pre> |
| */ |
| public void save(ResourceInfo info, IPathRequestor requestor, DataOutputStream output, List<String> writtenTypes) throws IOException { |
| // phantom resources don't have markers |
| if (info.isSet(ICoreConstants.M_PHANTOM)) |
| return; |
| MarkerSet markers = info.getMarkers(false); |
| if (markers == null) |
| return; |
| IMarkerSetElement[] elements = markers.elements(); |
| // filter out the markers...determine if there are any persistent ones |
| Object[] result = filterMarkers(elements); |
| int count = ((Integer) result[0]).intValue(); |
| if (count == 0) |
| return; |
| // if this is the first set of markers that we have written, then |
| // write the version id for the file. |
| if (output.size() == 0) |
| output.writeInt(MARKERS_SAVE_VERSION); |
| boolean[] isPersistent = (boolean[]) result[1]; |
| output.writeUTF(requestor.requestPath().toString()); |
| output.writeInt(count); |
| for (int i = 0; i < elements.length; i++) |
| if (isPersistent[i]) |
| write((MarkerInfo) elements[i], output, writtenTypes); |
| } |
| |
| /** |
| * Snapshot the markers for the specified resource to the given output stream. |
| * |
| * <pre> {@code |
| * SNAP_FILE -> [VERSION_ID RESOURCE]* |
| * VERSION_ID -> int (used for backwards compatibiliy) |
| * RESOURCE -> RESOURCE_PATH MARKER_SIZE MARKER+ |
| * RESOURCE_PATH -> String |
| * MARKER_SIZE -> int |
| * MARKER -> MARKER_ID TYPE ATTRIBUTES_SIZE ATTRIBUTE* CREATION_TIME |
| * MARKER_ID -> long |
| * TYPE -> INDEX | QNAME |
| * INDEX -> byte int |
| * QNAME -> byte String |
| * ATTRIBUTES_SIZE -> short |
| * ATTRIBUTE -> ATTRIBUTE_KEY ATTRIBUTE_VALUE |
| * ATTRIBUTE_KEY -> String |
| * ATTRIBUTE_VALUE -> BOOLEAN_VALUE | INTEGER_VALUE | STRING_VALUE | NULL_VALUE |
| * BOOLEAN_VALUE -> byte boolean |
| * INTEGER_VALUE -> byte int |
| * STRING_VALUE -> byte String |
| * NULL_VALUE -> byte |
| * CREATION_TIME -> long |
| * }</pre> |
| */ |
| public void snap(ResourceInfo info, IPathRequestor requestor, DataOutputStream output) throws IOException { |
| // phantom resources don't have markers |
| if (info.isSet(ICoreConstants.M_PHANTOM)) |
| return; |
| if (!info.isSet(ICoreConstants.M_MARKERS_SNAP_DIRTY)) |
| return; |
| MarkerSet markers = info.getMarkers(false); |
| if (markers == null) |
| return; |
| IMarkerSetElement[] elements = markers.elements(); |
| // filter out the markers...determine if there are any persistent ones |
| Object[] result = filterMarkers(elements); |
| int count = ((Integer) result[0]).intValue(); |
| // write the version id for the snapshot. |
| output.writeInt(MARKERS_SNAP_VERSION); |
| boolean[] isPersistent = (boolean[]) result[1]; |
| output.writeUTF(requestor.requestPath().toString()); |
| // always write out the count...even if its zero. this will help |
| // use pick up marker deletions from our snapshot. |
| output.writeInt(count); |
| List<String> writtenTypes = new ArrayList<>(); |
| for (int i = 0; i < elements.length; i++) |
| if (isPersistent[i]) |
| write((MarkerInfo) elements[i], output, writtenTypes); |
| info.clear(ICoreConstants.M_MARKERS_SNAP_DIRTY); |
| } |
| |
| /* |
| * Write out the given marker attributes to the given output stream. |
| */ |
| private void write(Map<String, Object> attributes, DataOutputStream output) throws IOException { |
| output.writeShort(attributes.size()); |
| for (Map.Entry<String, Object> e : attributes.entrySet()) { |
| String key = e.getKey(); |
| output.writeUTF(key); |
| Object value = e.getValue(); |
| if (value instanceof Integer) { |
| output.writeByte(ATTRIBUTE_INTEGER); |
| output.writeInt(((Integer) value).intValue()); |
| continue; |
| } |
| if (value instanceof Boolean) { |
| output.writeByte(ATTRIBUTE_BOOLEAN); |
| output.writeBoolean(((Boolean) value).booleanValue()); |
| continue; |
| } |
| if (value instanceof String) { |
| output.writeByte(ATTRIBUTE_STRING); |
| output.writeUTF((String) value); |
| continue; |
| } |
| // otherwise we came across an attribute of an unknown type |
| // so just write out null since we don't know how to marshal it. |
| output.writeByte(ATTRIBUTE_NULL); |
| } |
| } |
| |
| private void write(MarkerInfo info, DataOutputStream output, List<String> writtenTypes) throws IOException { |
| output.writeLong(info.getId()); |
| // if we have already written the type once, then write an integer |
| // constant to represent it instead to remove duplication |
| String type = info.getType(); |
| int index = writtenTypes.indexOf(type); |
| if (index == -1) { |
| output.writeByte(QNAME); |
| output.writeUTF(type); |
| writtenTypes.add(type); |
| } else { |
| output.writeByte(INDEX); |
| output.writeInt(index); |
| } |
| |
| // write out the size of the attribute table and |
| // then each attribute. |
| if (info.getAttributes(false) == null) { |
| output.writeShort(0); |
| } else |
| write(info.getAttributes(false), output); |
| |
| // write out the creation time |
| output.writeLong(info.getCreationTime()); |
| } |
| } |