blob: efd8d10d39b671738a7febe5a0adff508232e58b [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2009, 2019 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 <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.internal.nico.core;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.osgi.service.prefs.BackingStoreException;
import org.osgi.service.prefs.Preferences;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.osgi.util.NLS;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.ecommons.net.resourcemapping.core.IResourceMapping;
import org.eclipse.statet.ecommons.net.resourcemapping.core.IResourceMappingManager;
import org.eclipse.statet.ecommons.net.resourcemapping.core.ResourceMappingOrder;
import org.eclipse.statet.nico.core.NicoCore;
public class ResourceMappingManager implements IResourceMappingManager {
private static final String QUALIFIER= NicoCore.BUNDLE_ID + "/resoursemappings"; //$NON-NLS-1$
private static final String LOCAL_KEY= "local.path"; //$NON-NLS-1$
private static final String HOST_KEY= "host.name"; //$NON-NLS-1$
private static final String REMOTE_KEY= "remote.path"; //$NON-NLS-1$
public static final Comparator<IResourceMapping> DEFAULT_COMPARATOR= new Comparator<IResourceMapping>() {
@Override
public int compare(final IResourceMapping o1, final IResourceMapping o2) {
final int diff= o1.getHost().compareTo(o2.getHost());
if (diff != 0) {
return diff;
}
return o1.getRemotePath().toPortableString().compareTo(o2.getRemotePath().toPortableString());
}
};
private static final Comparator<IResourceMapping> LOCAL_COMPARATOR= new Comparator<IResourceMapping>() {
@Override
public int compare(final IResourceMapping o1, final IResourceMapping o2) {
return - o1.getFileStore().toURI().compareTo(o2.getFileStore().toURI());
}
};
private static final Comparator<IResourceMapping> REMOTE_COMPARATOR= new Comparator<IResourceMapping>() {
@Override
public int compare(final IResourceMapping o1, final IResourceMapping o2) {
return - o1.getRemotePath().toPortableString().compareTo(o2.getRemotePath().toPortableString());
}
};
private class UpdateJob extends Job {
UpdateJob() {
super("Update Resource Mappings");
setPriority(BUILD);
}
@Override
protected IStatus run(final IProgressMonitor monitor) {
final List<ResourceMapping> list= ResourceMappingManager.this.list;
if (list == null) {
return Status.OK_STATUS;
}
final SubMonitor m= SubMonitor.convert(monitor, list.size() + 1);
try {
final MultiStatus status= new MultiStatus(NicoCore.BUNDLE_ID, 0, "Update Resource Mapping", null);
final Map<String, List<IResourceMapping>[]> mappingsByHost= new HashMap<>();
for (final ResourceMapping mapping : list) {
try {
mapping.resolve();
final InetAddress[] addresses= mapping.getHostAddresses();
for (final InetAddress inetAddress : addresses) {
final String host= inetAddress.getHostAddress();
List<IResourceMapping>[] mappings= mappingsByHost.get(host);
if (mappings == null) {
mappings= new List[] { new ArrayList<>(), null };
mappingsByHost.put(host, mappings);
}
mappings[0].add(mapping);
}
}
catch (final UnknownHostException e) {
status.add(new Status(IStatus.INFO, NicoCore.BUNDLE_ID, "Unknown host: " + e.getMessage(), e));
}
finally {
m.worked(1);
}
}
for (final List<IResourceMapping>[] lists : mappingsByHost.values()) {
final List<IResourceMapping> unsorted= lists[0];
lists[ResourceMappingOrder.LOCAL.ordinal()]= ImCollections.toList(unsorted,
LOCAL_COMPARATOR );
lists[ResourceMappingOrder.REMOTE.ordinal()]= ImCollections.toList(unsorted,
REMOTE_COMPARATOR );
}
synchronized(ResourceMappingManager.this) {
if (ResourceMappingManager.this.list == list) {
ResourceMappingManager.this.mappingsByHost= mappingsByHost;
}
}
return status;
}
finally {
m.done();
}
}
}
private ImList<ResourceMapping> list;
private Map<String, List<IResourceMapping>[]> mappingsByHost;
private final UpdateJob updateJob;
public ResourceMappingManager() {
this.updateJob= new UpdateJob();
load();
}
public void dispose() {
synchronized (this) {
this.list= null;
this.updateJob.cancel();
}
}
public List<ResourceMapping> getList() {
return this.list;
}
public List<IResourceMapping> getMappingsFor(final String hostAddress, final ResourceMappingOrder order) {
final Map<String, List<IResourceMapping>[]> byHost= this.mappingsByHost;
if (byHost != null) {
final List<IResourceMapping>[] lists= byHost.get(hostAddress);
if (lists != null) {
return lists[(order != null) ? order.ordinal() : 0];
}
}
return null;
}
public void load() {
try {
final List<ResourceMapping> list= new ArrayList<>();
final IEclipsePreferences rootNode= InstanceScope.INSTANCE.getNode(QUALIFIER);
final String[] names= rootNode.childrenNames();
for (final String name : names) {
final ResourceMapping mapping= read(rootNode.node(name));
if (mapping != null) {
list.add(mapping);
}
}
final ResourceMapping[] array= list.toArray(new ResourceMapping[list.size()]);
Arrays.sort(array, DEFAULT_COMPARATOR);
synchronized (this) {
this.list= ImCollections.newList(array);
this.updateJob.cancel();
this.updateJob.schedule();
}
}
catch (final BackingStoreException e) {
NicoCorePlugin.logError(-1, "Failed to load resource mappings.", e);
}
}
public void setMappings(final List<ResourceMapping> list) {
final ImList<ResourceMapping> newMappings= ImCollections.toList(list, DEFAULT_COMPARATOR);
try {
final IEclipsePreferences rootNode= InstanceScope.INSTANCE.getNode(QUALIFIER);
final List<String> names= new LinkedList<>(ImCollections.newList(rootNode.childrenNames()));
final List<ResourceMapping> todo= new LinkedList<>(newMappings);
int maxIdx= 0;
for (final Iterator<ResourceMapping> iter= todo.iterator(); iter.hasNext(); ) {
final ResourceMapping mapping= iter.next();
final String id= mapping.getId();
if (id != null) {
try {
final int idx= Integer.parseInt(id);
if (idx > maxIdx) {
maxIdx= idx;
}
}
catch (final NumberFormatException e) {
}
iter.remove();
names.remove(id);
write(rootNode.node(id), mapping);
}
}
for (final Iterator<ResourceMapping> iter= todo.iterator(); iter.hasNext(); ) {
final ResourceMapping mapping= iter.next();
final String id= Integer.toString(++maxIdx);
mapping.setId(id);
names.remove(id);
write(rootNode.node(id), mapping);
}
for (final String name : names) {
if (rootNode.nodeExists(name)) {
final Preferences node= rootNode.node(name);
node.removeNode();
}
}
rootNode.flush();
synchronized (this) {
this.list= newMappings;
this.updateJob.cancel();
this.updateJob.schedule();
}
}
catch (final BackingStoreException e) {
NicoCorePlugin.logError(-1, "Failed to save resource mappings.", e);
}
}
protected ResourceMapping read(final Preferences node) {
final String id= node.name();
final String local= node.get(LOCAL_KEY, null);
final String host= node.get(HOST_KEY, null);
final String remote= node.get(REMOTE_KEY, null);
if (local != null && host != null && remote != null) {
try {
return new ResourceMapping(id, local, host, remote);
}
catch (final CoreException e) {
NicoCorePlugin.logError(-1, NLS.bind("Failed to load resource mapping: ''{0}''.", id), e);
}
}
return null;
}
protected void write(final Preferences node, final ResourceMapping mapping) {
node.put(LOCAL_KEY, mapping.getLocalText());
node.put(HOST_KEY, mapping.getHost());
node.put(REMOTE_KEY, mapping.getRemotePath().toString());
}
@Override
public List<IResourceMapping> getResourceMappingsFor(final String hostAddress, final ResourceMappingOrder order) {
final List<IResourceMapping> mappings= getMappingsFor(hostAddress, order);
if (mappings != null) {
return mappings;
}
return Collections.emptyList();
}
@Override
public IFileStore mapRemoteResourceToFileStore(final String hostAddress, IPath remotePath, final IPath relativeBasePath) {
if (!remotePath.isAbsolute()) {
if (relativeBasePath == null) {
return null;
}
remotePath= relativeBasePath.append(remotePath);
}
final List<IResourceMapping> mappings= getResourceMappingsFor(hostAddress, ResourceMappingOrder.REMOTE);
for (final IResourceMapping mapping : mappings) {
final IPath remoteBase= mapping.getRemotePath();
if (remoteBase.isPrefixOf(remotePath)) {
final IPath subPath= remotePath.removeFirstSegments(remoteBase.segmentCount());
final IFileStore localBaseStore= mapping.getFileStore();
return localBaseStore.getFileStore(subPath);
}
}
return null;
}
@Override
public IPath mapFileStoreToRemoteResource(final String hostAddress, final IFileStore fileStore) {
final List<IResourceMapping> mappings= getResourceMappingsFor(hostAddress, ResourceMappingOrder.LOCAL);
for (final IResourceMapping mapping : mappings) {
final IFileStore localBaseStore= mapping.getFileStore();
if (localBaseStore.equals(fileStore)) {
return mapping.getRemotePath();
}
if (localBaseStore.isParentOf(fileStore)) {
final IPath localBasePath= new Path(localBaseStore.toURI().getPath());
final IPath fileStorePath= new Path(fileStore.toURI().getPath());
if (localBasePath.isPrefixOf(fileStorePath)) {
final IPath subPath= fileStorePath.removeFirstSegments(localBasePath.segmentCount());
final IPath remotePath= mapping.getRemotePath();
return remotePath.append(subPath);
}
}
}
return null;
}
}