org.apache.jackrabbit.oak.plugins.mongomk.util.Utils Maven / Gradle / Ivy
/*
* 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.jackrabbit.oak.plugins.mongomk.util;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.mongodb.BasicDBObject;
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.plugins.mongomk.Revision;
import org.bson.types.ObjectId;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Utility methods.
*/
public class Utils {
/**
* Make sure the name string does not contain unnecessary baggage (shared
* strings).
*
* This is only needed for older versions of Java (before Java 7 update 6).
* See also
* http://mail.openjdk.java.net/pipermail/core-libs-dev/2012-May/010257.html
*
* @param x the string
* @return the new string
*/
public static String unshareString(String x) {
return new String(x);
}
public static int pathDepth(String path) {
if (path.equals("/")) {
return 0;
}
int depth = 0;
for (int i = 0; i < path.length(); i++) {
if (path.charAt(i) == '/') {
depth++;
}
}
return depth;
}
public static Map newMap() {
return new TreeMap();
}
public static Set newSet() {
return new HashSet();
}
@SuppressWarnings("unchecked")
public static int estimateMemoryUsage(Map map) {
if (map == null) {
return 0;
}
int size = 0;
for (Entry e : map.entrySet()) {
if (e.getKey() instanceof Revision) {
size += 32;
} else {
size += 48 + e.getKey().toString().length() * 2;
}
Object o = e.getValue();
if (o instanceof String) {
size += 48 + ((String) o).length() * 2;
} else if (o instanceof Long) {
size += 16;
} else if (o instanceof Boolean) {
size += 8;
} else if (o instanceof Integer) {
size += 8;
} else if (o instanceof Map) {
size += 8 + estimateMemoryUsage((Map) o);
} else if (o == null) {
// zero
} else {
throw new IllegalArgumentException("Can't estimate memory usage of " + o);
}
}
if (map instanceof BasicDBObject) {
// Based on empirical testing using JAMM
size += 176;
size += map.size() * 136;
} else {
// overhead for some other kind of map
// TreeMap (80) + unmodifiable wrapper (32)
size += 112;
// 64 bytes per entry
size += map.size() * 64;
}
return size;
}
/**
* Generate a unique cluster id, similar to the machine id field in MongoDB ObjectId objects.
*
* @return the unique machine id
*/
public static int getUniqueClusterId() {
ObjectId objId = new ObjectId();
return objId._machine();
}
public static String escapePropertyName(String propertyName) {
int len = propertyName.length();
if (len == 0) {
return "_";
}
// avoid creating a buffer if escaping is not needed
StringBuilder buff = null;
char c = propertyName.charAt(0);
int i = 0;
if (c == '_' || c == '$') {
buff = new StringBuilder(len + 1);
buff.append('_').append(c);
i++;
}
for (; i < len; i++) {
c = propertyName.charAt(i);
char rep;
switch (c) {
case '.':
rep = 'd';
break;
case '\\':
rep = '\\';
break;
default:
rep = 0;
}
if (rep != 0) {
if (buff == null) {
buff = new StringBuilder(propertyName.substring(0, i));
}
buff.append('\\').append(rep);
} else if (buff != null) {
buff.append(c);
}
}
return buff == null ? propertyName : buff.toString();
}
public static String unescapePropertyName(String key) {
int len = key.length();
if (key.startsWith("_")
&& (key.startsWith("__") || key.startsWith("_$") || len == 1)) {
key = key.substring(1);
len--;
}
// avoid creating a buffer if escaping is not needed
StringBuilder buff = null;
for (int i = 0; i < len; i++) {
char c = key.charAt(i);
if (c == '\\') {
if (buff == null) {
buff = new StringBuilder(key.substring(0, i));
}
c = key.charAt(++i);
if (c == '\\') {
// ok
} else if (c == 'd') {
c = '.';
}
buff.append(c);
} else if (buff != null) {
buff.append(c);
}
}
return buff == null ? key : buff.toString();
}
public static boolean isPropertyName(String key) {
return !key.startsWith("_") || key.startsWith("__") || key.startsWith("_$");
}
public static String getIdFromPath(String path) {
int depth = Utils.pathDepth(path);
return depth + ":" + path;
}
public static String getPathFromId(String id) {
int index = id.indexOf(':');
return id.substring(index + 1);
}
public static String getPreviousIdFor(String id, Revision r) {
return getIdFromPath("p" + PathUtils.concat(getPathFromId(id), r.toString()));
}
/**
* Deep copy of a map that may contain map values.
*
* @param source the source map
* @param target the target map
* @param the type of the map key
*/
public static void deepCopyMap(Map source, Map target) {
for (Entry e : source.entrySet()) {
Object value = e.getValue();
Comparator comparator = null;
if (value instanceof SortedMap) {
@SuppressWarnings("unchecked")
SortedMap map = (SortedMap) value;
comparator = map.comparator();
}
if (value instanceof Map) {
@SuppressWarnings("unchecked")
Map old = (Map) value;
Map c = new TreeMap(comparator);
deepCopyMap(old, c);
value = c;
}
target.put(e.getKey(), value);
}
}
/**
* Returns the lower key limit to retrieve the children of the given
* path
.
*
* @param path a path.
* @return the lower key limit.
*/
public static String getKeyLowerLimit(String path) {
String from = PathUtils.concat(path, "a");
from = getIdFromPath(from);
from = from.substring(0, from.length() - 1);
return from;
}
/**
* Returns the upper key limit to retrieve the children of the given
* path
.
*
* @param path a path.
* @return the upper key limit.
*/
public static String getKeyUpperLimit(String path) {
String to = PathUtils.concat(path, "z");
to = getIdFromPath(to);
to = to.substring(0, to.length() - 2) + "0";
return to;
}
/**
* Returns true
if a revision tagged with the given revision
* should be considered committed, false
otherwise. Committed
* revisions have a tag, which equals 'c' or starts with 'c-'.
*
* @param tag the tag (may be null
).
* @return true
if committed; false
otherwise.
*/
public static boolean isCommitted(@Nullable String tag) {
return tag != null && (tag.equals("c") || tag.startsWith("c-"));
}
/**
* Resolve the commit revision for the given revision rev
and
* the associated commit tag.
*
* @param rev a revision.
* @param tag the associated commit tag.
* @return the actual commit revision for rev
.
*/
@Nonnull
public static Revision resolveCommitRevision(@Nonnull Revision rev,
@Nonnull String tag) {
return checkNotNull(tag).startsWith("c-") ?
Revision.fromString(tag.substring(2)) : rev;
}
}