[Workspace] Add util for marker bulk updates

Change-Id: I9206af407dad957ba17616df7216e557b105bd1e
diff --git a/ecommons/org.eclipse.statet.ecommons.coremisc/src/org/eclipse/statet/ecommons/resources/core/util/MarkerUpdate.java b/ecommons/org.eclipse.statet.ecommons.coremisc/src/org/eclipse/statet/ecommons/resources/core/util/MarkerUpdate.java
new file mode 100644
index 0000000..0e1406f
--- /dev/null
+++ b/ecommons/org.eclipse.statet.ecommons.coremisc/src/org/eclipse/statet/ecommons/resources/core/util/MarkerUpdate.java
@@ -0,0 +1,98 @@
+/*=============================================================================#
+ # Copyright (c) 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.ecommons.resources.core.util;
+
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
+
+import java.util.Arrays;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.statet.jcommons.lang.NonNull;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+
+@NonNullByDefault
+public class MarkerUpdate {
+	
+	
+	private final IMarker marker;
+	
+	private @NonNull String[] names;
+	private @Nullable Object[] values;
+	private int n;
+	
+	
+	public MarkerUpdate(final IMarker marker, final int initialCapacity) {
+		this.marker= nonNullAssert(marker);
+		
+		this.names= new @NonNull String[initialCapacity];
+		this.values= new @Nullable Object[initialCapacity];
+		this.n= 0;
+	}
+	
+	
+	public IMarker getMarker() {
+		return this.marker;
+	}
+	
+	
+	private void ensureCapacity(final int n) {
+		if (n <= this.names.length) {
+			return;
+		}
+		final int l= Math.max(n, this.names.length + 8);
+		this.names= Arrays.copyOf(this.names, l);
+		this.values= Arrays.copyOf(this.values, l);
+	}
+	
+	public void setAttribute(final String name, final @Nullable Object value) {
+		final int idx= this.n;
+		ensureCapacity(idx + 1);
+		this.names[idx]= nonNullAssert(name);
+		this.values[idx]= value;
+		this.n++;
+	}
+	
+	public void setAttribute(final String name, final int value) {
+		setAttribute(name, Integer.valueOf(value));
+	}
+	
+	public void setAttribute(final String name, final boolean value) {
+		setAttribute(name, Boolean.valueOf(value));
+	}
+	
+	public void removeAttribute(final String name) {
+		setAttribute(name, null);
+	}
+	
+	
+	public void apply() throws CoreException {
+		final int n= this.n;
+		if (n == 0) {
+			return;
+		}
+		var names= this.names;
+		var values= this.values;
+		if (n != names.length) {
+			names= Arrays.copyOf(names, n);
+			values= Arrays.copyOf(values, n);
+		}
+		this.marker.setAttributes(names, values);
+	}
+	
+}