/*******************************************************************************
* Copyright (c) 2012, 2015 Wind River Systems, Inc. and others. All rights reserved.
* This program and the accompanying materials are made available under the terms
* of the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.te.tcf.filesystem.core.internal.utils;
import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.tcf.te.runtime.persistence.delegates.GsonMapPersistenceDelegate;
/**
* The persistence delegate to persist or restore a map whose keys are URIs.
*/
public class PersistenceManagerDelegate extends GsonMapPersistenceDelegate {
private static final String MAP_KEY_MTIME = "mtime"; //$NON-NLS-1$
private static final String MAP_KEY_TARGET_MTIME = "target_mtime"; //$NON-NLS-1$
private static final String MAP_KEY_TARGET = "target"; //$NON-NLS-1$
private static final String MAP_KEY_CACHE = "cache"; //$NON-NLS-1$
private static final String MAP_KEY_BASE = "base"; //$NON-NLS-1$
private static final String MAP_KEY_UNRESOLVED = "unresolved"; //$NON-NLS-1$
private static final String MAP_KEY_RESOLVED = "resolved"; //$NON-NLS-1$
private static final String MAP_KEY_PROPERTIES = "properties"; //$NON-NLS-1$
private static final String MAP_KEY_DIGESTS = "digests"; //$NON-NLS-1$
/*
* (non-Javadoc)
* @see
* org.eclipse.tcf.te.runtime.persistence.interfaces.IPersistenceDelegate#getPersistedClass(
* java.lang.Object)
*/
@Override
public Class<?> getPersistedClass(Object context) {
return PersistenceManager.class;
}
/*
* (non-Javadoc)
* @see
* org.eclipse.tcf.te.runtime.persistence.AbstractGsonMapPersistenceDelegate#toMap(java.lang
* .Object)
*/
@Override
protected Map<String, Object> toMap(Object context) throws IOException {
PersistenceManager pMgr = (PersistenceManager) context;
Map<String, Object> result = new HashMap<String, Object>();
result.put(MAP_KEY_DIGESTS, persistDigests(pMgr.digests));
result.put(MAP_KEY_PROPERTIES, persistProperties(pMgr.properties));
result.put(MAP_KEY_RESOLVED, persistResolved(pMgr.resolved));
result.put(MAP_KEY_UNRESOLVED, persistUnresolved(pMgr.unresolved));
return result;
}
private Object persistProperties(Map<URI, Map<QualifiedName, String>> properties) {
Map<String, Object> result = new HashMap<String, Object>();
if (properties != null) {
for (Entry<URI, Map<QualifiedName, String>> entry : properties.entrySet()) {
Map<QualifiedName, String> map = entry.getValue();
Map<String, Object> valueMap = qNames2Map(map);
result.put(entry.getKey().toString(), valueMap);
}
}
return result;
}
private Object persistResolved(Map<URI, IContentType> resolved) {
Map<String, Object> result = new HashMap<String, Object>();
if (resolved != null) {
for (Entry<URI, IContentType> entry : resolved.entrySet()) {
IContentType object = entry.getValue();
String value = object.getId();
result.put(entry.getKey().toString(), value);
}
}
return result;
}
private Object persistUnresolved(Map<URI, URI> unresolved) {
Map<String, Object> result = new HashMap<String, Object>();
if (unresolved != null) {
for (Entry<URI, URI> entry : unresolved.entrySet()) {
URI uri = entry.getValue();
String value = uri.toString();
result.put(entry.getKey().toString(), value);
}
}
return result;
}
private Object persistDigests(Map<URI, FileState> states) {
Map<String, Object> result = new HashMap<String, Object>();
if (states != null) {
for (Entry<URI, FileState> entry : states.entrySet()) {
FileState fileState = entry.getValue();
Map<String, Object> value = digest2map(fileState);
result.put(entry.getKey().toString(), value);
}
}
return result;
}
/**
* Translate the specified map whose keys are QualifiedNames to a map whose keys are strings.
*
* @param map The map to be translated.
* @return a map with string keys.
*/
private Map<String, Object> qNames2Map(Map<QualifiedName, String> map) {
Map<String, Object> result = new HashMap<String, Object>();
for (Entry<QualifiedName, String> entry : map.entrySet()) {
result.put(entry.getKey().toString(), entry.getValue());
}
return result;
}
/*
* (non-Javadoc)
* @see
* org.eclipse.tcf.te.runtime.persistence.AbstractGsonMapPersistenceDelegate#fromMap(java.util
* .Map, java.lang.Object)
*/
@SuppressWarnings("unchecked")
@Override
protected Object fromMap(Map<String, Object> map, Object context) throws IOException {
PersistenceManager result = (PersistenceManager) context;
Map<String, Map<String, Object>> digests = (Map<String, Map<String, Object>>) map.get(MAP_KEY_DIGESTS);
Map<String, Map<String, String>> properties = (Map<String, Map<String, String>>) map.get(MAP_KEY_PROPERTIES);
Map<String, String> resolved = (Map<String, String>) map.get(MAP_KEY_RESOLVED);
Map<String, String> unresolved = (Map<String, String>) map.get(MAP_KEY_UNRESOLVED);
restoreDigests(digests, result.digests);
restoreProperites(properties, result.properties);
restoreResolved(resolved, result.resolved);
restoreUnresolved(unresolved, result.unresolved);
return result;
}
private void restoreUnresolved(Map<String, String> map, Map<URI, URI> unresolved) {
for (Entry<String, String> entry : map.entrySet()) {
String value = entry.getValue();
URI uri = toURI(entry.getKey());
Assert.isNotNull(uri);
uri = toURI(value);
Assert.isNotNull(uri);
unresolved.put(uri, uri);
}
}
private void restoreResolved(Map<String, String> map, Map<URI, IContentType> contentTypes) {
for (Entry<String, String> entry : map.entrySet()) {
String value = entry.getValue();
URI uri = toURI(entry.getKey());
Assert.isNotNull(uri);
IContentType contentType = Platform.getContentTypeManager().getContentType(value);
contentTypes.put(uri, contentType);
}
}
private void restoreDigests(Map<String, Map<String, Object>> map, Map<URI, FileState> states) {
for (Entry<String, Map<String, Object>> entry : map.entrySet()) {
Map<String, Object> value = entry.getValue();
URI uri = toURI(entry.getKey());
Assert.isNotNull(uri);
FileState digest = map2digest(value);
states.put(uri, digest);
}
}
private void restoreProperites(Map<String, Map<String, String>> map, Map<URI, Map<QualifiedName, String>> properties) {
for (Entry<String, Map<String, String>> entry : map.entrySet()) {
Map<String, String> value = entry.getValue();
URI uri = toURI(entry.getKey());
Assert.isNotNull(uri);
Map<QualifiedName, String> valueMap = toQNameMap(value);
properties.put(uri, valueMap);
}
}
private FileState map2digest(Map<String, Object> value) {
byte[] base_digest = string2digest((String) value.get(MAP_KEY_BASE));
byte[] cache_digest = string2digest((String) value.get(MAP_KEY_CACHE));
byte[] target_digest = string2digest((String) value.get(MAP_KEY_TARGET));
Number number = (Number) value.get(MAP_KEY_MTIME);
long mtime = number.longValue();
number = (Number) value.get(MAP_KEY_TARGET_MTIME);
long target_mtime = number == null ? 0 : number.longValue();
return new FileState(mtime, target_mtime, cache_digest, target_digest, base_digest);
}
private Map<String, Object> digest2map(FileState digest) {
Map<String, Object> map = new HashMap<String, Object>();
map.put(MAP_KEY_BASE, digest2string(digest.getBaseDigest()));
map.put(MAP_KEY_CACHE, digest2string(digest.getCacheDigest()));
map.put(MAP_KEY_TARGET, digest2string(digest.getTargetDigest()));
map.put(MAP_KEY_MTIME, Long.valueOf(digest.getCacheMTime()));
map.put(MAP_KEY_TARGET_MTIME, Long.valueOf(digest.getTargetMTime()));
return map;
}
static String digest2string(byte[] digest) {
if (digest != null && digest.length > 0) {
StringBuilder buffer = new StringBuilder();
for (byte element : digest) {
int d = element & 0xff;
String sByte = Integer.toHexString(d);
while (sByte.length() < 2)
{
sByte = "0" + sByte; //$NON-NLS-1$
}
buffer.append(sByte.toLowerCase());
}
return buffer.toString();
}
return ""; //$NON-NLS-1$
}
private byte[] string2digest(String string) {
if (string != null && string.length() > 0) {
int count = string.length() / 2;
byte[] digest = new byte[count];
for (int i = 0; i < count; i++) {
try {
String seg = string.substring(2 * i, 2 * (i + 1));
int d = Integer.parseInt(seg, 16);
digest[i] = (byte) d;
}
catch (Exception e) {
}
}
return digest;
}
return new byte[0];
}
/**
* Translate the specified map with string keys to a map whose keys are qualified names.
*
* @param strMap The map with string keys.
* @return A map with qualified names as keys.
*/
private Map<QualifiedName, String> toQNameMap(Map<String, String> strMap) {
Map<QualifiedName, String> result = new HashMap<QualifiedName, String>();
for (Entry<String, String> entry : strMap.entrySet()) {
int dot = entry.getKey().lastIndexOf(":"); //$NON-NLS-1$
String qualifier = null;
String local = entry.getKey();
if (dot != -1) {
qualifier = entry.getKey().substring(0, dot);
local = entry.getKey().substring(dot + 1);
}
QualifiedName name = new QualifiedName(qualifier, local);
result.put(name, strMap.get(entry.getKey()));
}
return result;
}
/**
* Convert the string to a URI.
*
* @param string The string to be converted.
* @return the URI or null if there're issues when parsing.
*/
private URI toURI(final String string) {
final AtomicReference<URI> ref = new AtomicReference<URI>();
SafeRunner.run(new ISafeRunnable() {
@Override
public void handleException(Throwable exception) {
// Ignore on purpose.
}
@Override
public void run() throws Exception {
ref.set(new URI(string));
}
});
return ref.get();
}
}