/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * 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: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.ide.resources.impl; import com.google.common.annotations.Beta; import com.google.common.base.Optional; import com.google.common.collect.Maps; import com.google.inject.Inject; import org.eclipse.che.ide.api.resources.Container; import org.eclipse.che.ide.api.resources.Resource; import org.eclipse.che.ide.api.resources.ResourceInterceptor; import org.eclipse.che.ide.resource.Path; import java.util.Comparator; import java.util.Map; import java.util.Set; import static com.google.common.base.Optional.absent; import static com.google.common.base.Optional.of; import static com.google.common.base.Preconditions.checkArgument; import static java.lang.System.arraycopy; import static java.util.Arrays.binarySearch; import static java.util.Arrays.copyOf; /** * In memory implementation of {@link ResourceStore}. * * @author Vlad Zhukovskiy * @see ResourceStore * @since 4.4.0 */ @Beta class InMemoryResourceStore implements ResourceStore { private static final Resource[] EMPTY_RESOURCES = new Resource[0]; private static final Comparator<Resource> NAME_COMPARATOR = new Comparator<Resource>() { @Override public int compare(Resource o1, Resource o2) { return o1.getName().compareTo(o2.getName()); } }; private Map<Path, Resource[]> memoryCache; private Set<ResourceInterceptor> resourceInterceptors; @Inject public InMemoryResourceStore(Set<ResourceInterceptor> resourceInterceptors) { this.resourceInterceptors = resourceInterceptors; memoryCache = Maps.newHashMap(); } /** {@inheritDoc} */ @Override public boolean register(Resource resource) { checkArgument(resource != null, "Null resource occurred"); final Path parent = resource.getLocation().segmentCount() == 1 ? Path.ROOT : resource.getLocation().parent(); if (!memoryCache.containsKey(parent)) { memoryCache.put(parent, new Resource[]{resource}); intercept(resource); return true; } else { Resource[] container = memoryCache.get(parent); final int index = binarySearch(container, resource, NAME_COMPARATOR); if (index >= 0) { //update existing resource with new one container[index] = resource; intercept(resource); return false; } else { //such resource doesn't exists, then simply add it final int posIndex = -index - 1; //negate inverted index into positive one final int size = container.length; final Resource[] tmpContainer = copyOf(container, size + 1); arraycopy(tmpContainer, posIndex, tmpContainer, posIndex + 1, size - posIndex); //prepare cell to insert tmpContainer[posIndex] = resource; container = tmpContainer; memoryCache.put(parent, container); intercept(resource); return true; } } } /** {@inheritDoc} */ @Override public void dispose(Path path, boolean withChildren) { checkArgument(path != null, "Null path occurred"); final Path parent = path.segmentCount() == 1 ? Path.ROOT : path.parent(); Resource[] container = memoryCache.get(parent); if (container != null) { int index = -1; for (int i = 0; i < container.length; i++) { if (container[i].getName().equals(path.lastSegment())) { index = i; break; } } if (index != -1) { int size = container.length; int numMoved = container.length - index - 1; if (numMoved > 0) { System.arraycopy(container, index + 1, container, index, numMoved); } container = copyOf(container, --size); memoryCache.put(parent, container); } } if (memoryCache.containsKey(path)) { container = memoryCache.remove(path); if (container != null && withChildren) { for (Resource resource : container) { if (resource instanceof Container) { dispose(resource.getLocation(), true); } } } } } /** {@inheritDoc} */ @Override public Optional<Resource> getResource(Path path) { checkArgument(path != null, "Null path occurred"); final Path parent = path.parent(); if (!memoryCache.containsKey(parent)) { return absent(); } final Resource[] container = memoryCache.get(parent); if (container == null) { return absent(); } for (Resource resource : container) { if (resource.getLocation().equals(path)) { return Optional.of(resource); } } return absent(); } /** {@inheritDoc} */ @Override public Optional<Resource[]> get(Path parent) { checkArgument(parent != null, "Null path occurred"); if (!memoryCache.containsKey(parent)) { return absent(); } return of(memoryCache.get(parent)); } /** {@inheritDoc} */ @Override public Optional<Resource[]> getAll(Path parent) { checkArgument(parent != null, "Null path occurred"); if (!memoryCache.containsKey(parent)) { return absent(); } Resource[] all = new Resource[0]; for (Map.Entry<Path, Resource[]> setEntry : memoryCache.entrySet()) { /* There is no need to check compared path if its segment count is less then given one. */ final Path comparedPath = setEntry.getKey(); if (!parent.isPrefixOf(comparedPath)) { continue; } final Resource[] resources = setEntry.getValue(); if (resources == null || resources.length == 0) { continue; } final Resource[] tmpResourcesArr = copyOf(all, all.length + resources.length); arraycopy(resources, 0, tmpResourcesArr, all.length, resources.length); all = tmpResourcesArr; } if (all.length == 0) { return of(EMPTY_RESOURCES); } return of(all); } /** {@inheritDoc} */ @Override public void clear() { memoryCache.clear(); } private <R extends Resource> void intercept(R resource) { checkArgument(resource != null, "Null resource occurred"); resource.deleteAllMarkers(); for (ResourceInterceptor interceptor : resourceInterceptors) { interceptor.intercept(resource); } } }