| /* |
| * Copyright 2000-2011 JetBrains s.r.o. |
| * |
| * Licensed 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 com.intellij.openapi.diff.impl.dir; |
| |
| import com.intellij.ide.diff.*; |
| import com.intellij.util.containers.HashMap; |
| import com.intellij.util.containers.SortedList; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.io.IOException; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Comparator; |
| |
| import static com.intellij.ide.diff.DiffType.ERROR; |
| |
| /** |
| * @author Konstantin Bulenkov |
| */ |
| public class DTree { |
| private static final Comparator<DTree> COMPARATOR = new Comparator<DTree>() { |
| @Override |
| public int compare(DTree o1, DTree o2) { |
| final boolean b1 = o1.isContainer(); |
| final boolean b2 = o2.isContainer(); |
| return (b1 && b2) || (!b1 && !b2) |
| ? o1.getName().compareToIgnoreCase(o2.getName()) |
| : b1 ? 1 : -1; |
| } |
| }; |
| |
| private boolean myExpanded = true; |
| @Nullable private final DTree myParent; |
| private HashMap<String, DTree> myChildren; |
| private String myName; |
| private final boolean isContainer; |
| private SortedList<DTree> myChildrenList; |
| private DiffElement<?> mySource; |
| private DiffElement<?> myTarget; |
| private DiffType myType; |
| private boolean myVisible = true; |
| private String mySeparator = null; |
| private String myPath = null; |
| |
| public DTree(@Nullable DTree parent, @NotNull String name, boolean container) { |
| this.myParent = parent; |
| this.myName = name; |
| isContainer = container; |
| } |
| |
| @NotNull |
| public Collection<DTree> getChildren() { |
| init(); |
| if (myChildrenList == null) { |
| myChildrenList = new SortedList<DTree>(COMPARATOR); |
| myChildrenList.addAll(myChildren.values()); |
| } |
| return myChildrenList; |
| } |
| |
| public DTree addChild(@NotNull DiffElement element, boolean source) { |
| init(); |
| myChildrenList = null; |
| final DTree node; |
| final String name = element.getName(); |
| if (myChildren.containsKey(name)) { |
| node = myChildren.get(name); |
| } else { |
| node = new DTree(this, name, element.isContainer()); |
| myChildren.put(name, node); |
| } |
| |
| if (source) { |
| node.setSource(element); |
| } else { |
| node.setTarget(element); |
| } |
| |
| return node; |
| } |
| |
| public DiffElement<?> getSource() { |
| return mySource; |
| } |
| |
| public void setSource(DiffElement<?> source) { |
| mySource = source; |
| } |
| |
| public DiffElement<?> getTarget() { |
| return myTarget; |
| } |
| |
| public void setTarget(DiffElement<?> target) { |
| myTarget = target; |
| } |
| |
| private void init() { |
| if (myChildren == null) { |
| myChildren = new HashMap<String, DTree>(); |
| } |
| } |
| |
| public String getName() { |
| return myName; |
| } |
| |
| @Nullable |
| public DTree getParent() { |
| return myParent; |
| } |
| |
| public boolean isExpanded() { |
| return myExpanded; |
| } |
| |
| public void setExpanded(boolean expanded) { |
| this.myExpanded = expanded; |
| } |
| |
| public boolean isContainer() { |
| return isContainer; |
| } |
| |
| @Override |
| public String toString() { |
| return myName; |
| } |
| |
| public void update(DirDiffSettings settings) { |
| for (DTree tree : getChildren()) { |
| final DiffElement<?> src = tree.getSource(); |
| final DiffElement<?> trg = tree.getTarget(); |
| if (src instanceof DiffErrorElement || trg instanceof DiffErrorElement) { |
| tree.setType(ERROR); |
| } else if (src == null && trg != null) { |
| tree.setType(DiffType.TARGET); |
| } else if (src != null && trg == null) { |
| tree.setType(DiffType.SOURCE); |
| } else { |
| assert src != null; |
| DiffType dtype = src.getSize() == trg.getSize() ? DiffType.EQUAL : DiffType.CHANGED; |
| if (dtype == DiffType.EQUAL) { |
| switch (settings.compareMode) { |
| case CONTENT: |
| dtype = isEqual(src, trg) ? DiffType.EQUAL : DiffType.CHANGED; |
| break; |
| case TIMESTAMP: |
| dtype = Math.abs(src.getTimeStamp() - trg.getTimeStamp()) <= settings.compareTimestampAccuracy ? DiffType.EQUAL : DiffType.CHANGED; |
| break; |
| } |
| } |
| tree.setType(dtype); |
| } |
| tree.update(settings); |
| } |
| } |
| |
| public boolean isVisible() { |
| return myVisible; |
| } |
| |
| public void updateVisibility(DirDiffSettings settings) { |
| if (getChildren().isEmpty()) { |
| if (myType == ERROR) { |
| myVisible = true; |
| return; |
| } |
| if (myType != DiffType.SEPARATOR && !"".equals(settings.getFilter())) { |
| if (!settings.getFilterPattern().matcher(getName()).matches()) { |
| myVisible = false; |
| return; |
| } |
| } |
| if (myType == null) { |
| myVisible = true; |
| } else { |
| switch (myType) { |
| case SOURCE: |
| myVisible = settings.showNewOnSource; |
| break; |
| case TARGET: |
| myVisible = settings.showNewOnTarget; |
| break; |
| case SEPARATOR: |
| case ERROR: |
| myVisible = true; |
| break; |
| case CHANGED: |
| myVisible = settings.showDifferent; |
| break; |
| case EQUAL: |
| myVisible = settings.showEqual; |
| break; |
| } |
| } |
| } else { |
| myVisible = false; |
| for (DTree child : myChildren.values()) { |
| child.updateVisibility(settings); |
| myVisible = myVisible || child.isVisible(); |
| } |
| } |
| } |
| |
| public void reset() { |
| myChildren.clear(); |
| } |
| |
| public void remove(DTree node) { |
| init(); |
| final boolean removed = myChildrenList.remove(node); |
| if (removed) { |
| for (String key : myChildren.keySet()) { |
| if (myChildren.get(key) == node) { |
| myChildren.remove(key); |
| return; |
| } |
| } |
| } |
| } |
| |
| private static boolean isEqual(DiffElement file1, DiffElement file2) { |
| if (file1.isContainer() || file2.isContainer()) return false; |
| if (file1.getSize() != file2.getSize()) return false; |
| try { |
| return Arrays.equals(file1.getContent(), file2.getContent()); |
| } |
| catch (IOException e) { |
| return false; |
| } |
| } |
| |
| public DiffType getType() { |
| return myType; |
| } |
| |
| public void setType(DiffType type) { |
| this.myType = type; |
| } |
| |
| public String getPath() { |
| if (myPath == null) { |
| final DTree parent = getParent(); |
| if (parent != null) { |
| myPath = parent.getPath() + getName() + (isContainer ? getSeparator() : ""); |
| } else { |
| myPath = getName() + (isContainer ? getSeparator() : ""); |
| } |
| } |
| return myPath; |
| } |
| |
| private String getSeparator() { |
| if (mySeparator == null) { |
| mySeparator = mySource != null ? mySource.getSeparator() : myTarget != null ? myTarget.getSeparator() : ""; |
| } |
| return mySeparator; |
| } |
| } |