/******************************************************************************* * Copyright (C) 2011, 2013 Dariusz Luksza <dariusz@luksza.org> 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 *******************************************************************************/ package org.eclipse.egit.core.synchronize; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.eclipse.egit.core.internal.CoreText; import org.eclipse.egit.core.synchronize.ThreeWayDiffEntry.ChangeType; import org.eclipse.osgi.util.NLS; /** * Thin cache object. It contains list of object members, object name and * {@link ThreeWayDiffEntry} data. */ class GitSyncObjectCache { private final String name; private ThreeWayDiffEntry diffEntry; private Map<String, GitSyncObjectCache> members; /** * Creates node and leaf element * * @param name * entry name * @param diffEntry * entry meta data */ GitSyncObjectCache(String name, ThreeWayDiffEntry diffEntry) { this.name = name; this.diffEntry = diffEntry; } /** * @return name of this object */ public String getName() { return name; } /** * * @return entry meta data */ public ThreeWayDiffEntry getDiffEntry() { return diffEntry; } /** * Store given {@code entry} in cache. It assumes that parent of * {@code entry} is already in cache, if not {@link RuntimeException} will * be thrown. * * @param entry * that should be stored in cache * @throws RuntimeException * when cannot find parent of given {@code entry} in cache */ public void addMember(ThreeWayDiffEntry entry) { String memberPath = entry.getPath(); if (members == null) members = new HashMap<String, GitSyncObjectCache>(); int start = -1; Map<String, GitSyncObjectCache> parent = members; int separatorIdx = memberPath.indexOf("/"); //$NON-NLS-1$ while (separatorIdx > 0) { String key = memberPath.substring(start + 1, separatorIdx); GitSyncObjectCache cacheObject = parent.get(key); if (cacheObject == null) throw new RuntimeException(NLS.bind( CoreText.GitSyncObjectCache_noData, key)); start = separatorIdx; separatorIdx = memberPath.indexOf("/", separatorIdx + 1); //$NON-NLS-1$ if (cacheObject.members == null) cacheObject.members = new HashMap<String, GitSyncObjectCache>(); parent = cacheObject.members; } String newName; if (start > 0) newName = memberPath.substring(start + 1); else newName = memberPath; GitSyncObjectCache obj = new GitSyncObjectCache(newName, entry); parent.put(newName, obj); } /** * @param childPath * repository relative path of entry that should be obtained * @return cached object, or {@code null} when cache doen't contain object * for given path */ public GitSyncObjectCache get(String childPath) { if (childPath.length() == 0) return this; if (childPath .substring(childPath.lastIndexOf("/") + 1, childPath.length()).equals(name)) //$NON-NLS-1$ return this; if (members == null) return null; int start = -1; Map<String, GitSyncObjectCache> parent = members; int separatorIdx = childPath.indexOf("/"); //$NON-NLS-1$ while (separatorIdx > 0) { String key = childPath.substring(start + 1, separatorIdx); GitSyncObjectCache childObject = parent.get(key); if (childObject == null) return null; start = separatorIdx; separatorIdx = childPath.indexOf("/", separatorIdx + 1); //$NON-NLS-1$ parent = childObject.members; if (parent == null) return null; } return parent.get(childPath.subSequence( childPath.lastIndexOf("/") + 1, childPath.length())); //$NON-NLS-1$ } /** * @return number of cached members */ public int membersCount() { return members != null ? members.size() : 0; } /** * @return list of all cached members or {@code null} when there this object * doesn't contain members */ public Collection<GitSyncObjectCache> members() { return members != null ? members.values() : null; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("entry: ").append(diffEntry).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ if (members != null) { builder.append("members: "); //$NON-NLS-1$ for (GitSyncObjectCache obj : members.values()) builder.append(obj.toString()).append("\n"); //$NON-NLS-1$ } return builder.toString(); } void merge(GitSyncObjectCache other, Set<String> filterPaths) { if (other.members != null) { if (members == null) members = new HashMap<String, GitSyncObjectCache>(); for (Entry<String, GitSyncObjectCache> entry : members.entrySet()) { String key = entry.getKey(); if (!other.members.containsKey(key)) { GitSyncObjectCache obj = entry.getValue(); String entryPath = obj.getDiffEntry().getPath(); if (containsPathOrParent(filterPaths, entryPath)) obj.getDiffEntry().changeType = ChangeType.IN_SYNC; } } for (Entry<String, GitSyncObjectCache> entry : other.members .entrySet()) { String key = entry.getKey(); GitSyncObjectCache obj = entry.getValue(); if (members.containsKey(key)) { members.get(key).merge(obj, filterPaths); } else { members.put(key, obj); } } } else if (members != null) { for (GitSyncObjectCache obj : members.values()) { String entryPath = obj.getDiffEntry().getPath(); if (containsPathOrParent(filterPaths, entryPath)) obj.getDiffEntry().changeType = ChangeType.IN_SYNC; } } else { // we are on a leaf entry, use the newer diff entry (need to update // changeType, direction and other fields) diffEntry = other.diffEntry; } } private static boolean containsPathOrParent(Set<String> filterPaths, String pathToTest) { if (filterPaths.contains(pathToTest)) return true; // "" is the root directory of the repository, so it matches everything if (filterPaths.contains("")) //$NON-NLS-1$ return true; String path = pathToTest; int lastSlash = path.lastIndexOf('/'); while (lastSlash != -1) { path = path.substring(0, lastSlash); if (filterPaths.contains(path)) return true; lastSlash = path.lastIndexOf('/'); } return false; } }