0001: /*
0002: * Gruntspud
0003: *
0004: * Copyright (C) 2002 Brett Smith.
0005: *
0006: * Written by: Brett Smith <t_magicthize@users.sourceforge.net>
0007: *
0008: * This program is free software; you can redistribute it and/or
0009: * modify it under the terms of the GNU Library General Public License
0010: * as published by the Free Software Foundation; either version 2 of
0011: * the License, or (at your option) any later version.
0012: * This program is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0015: * GNU Library General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU Library General Public
0018: * License along with this program; if not, write to the Free Software
0019: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
0020: */
0021:
0022: package gruntspud;
0023:
0024: import gruntspud.file.DirectoryStatus;
0025: import gruntspud.filter.CVSFileFilter;
0026: import gruntspud.project.Project;
0027: import gruntspud.style.TextStyle;
0028: import gruntspud.ui.UIUtil;
0029: import gruntspud.ui.icons.OverlayIcon;
0030:
0031: import java.awt.Color;
0032: import java.io.File;
0033: import java.io.IOException;
0034: import java.util.Collections;
0035: import java.util.Date;
0036: import java.util.Enumeration;
0037: import java.util.HashMap;
0038: import java.util.Iterator;
0039: import java.util.Vector;
0040:
0041: import javax.swing.Icon;
0042: import javax.swing.SwingConstants;
0043: import javax.swing.tree.MutableTreeNode;
0044: import javax.swing.tree.TreeNode;
0045:
0046: import org.netbeans.lib.cvsclient.admin.DateComparator;
0047: import org.netbeans.lib.cvsclient.admin.Entry;
0048: import org.netbeans.lib.cvsclient.file.FileStatus;
0049: import org.netbeans.lib.cvsclient.util.IgnoreFileFilter;
0050:
0051: /**
0052: * This holds everything about a single node in the file heirarchy such if it is in CVS (or not),
0053: * all of its children (if any), its status, if it has a file that exists associated with it etc etc.
0054: * If the node can have children, the list is not actually loaded until a client requests for the
0055: * number of children available. This prevents the entire directory structure from being loaded upon
0056: * creation of the root node, nodes are only loaded when the parent is expanded by the user.
0057: *
0058: *@author magicthize
0059: *@created 26 May 2002
0060: */
0061: public class CVSFileNode implements MutableTreeNode, Comparable {
0062: public final static int SORT_ON_STATUS = 0;
0063: public final static int SORT_ON_KEYWORD_SUBST = 1;
0064: public final static int SORT_ON_NAME = 2;
0065: public final static int SORT_ON_TYPE = 3;
0066: public final static int SORT_ON_REVISION = 4;
0067: public final static int SORT_ON_DATE = 5;
0068: public final static int SORT_ON_FLAGS = 6;
0069: public final static int SORT_ON_SIZE = 7;
0070: public final static int SORT_ON_LOCAL_STATUS = 8;
0071: public final static int SORT_ON_REMOTE_STATUS = 9;
0072: public final static int SORT_ON_TAG = 10;
0073:
0074: public final static int LINE_ENDINGS_UNCHECKED = -1;
0075: public final static int WINDOWS_LINE_ENDINGS = 0;
0076: public final static int UNIX_LINE_ENDINGS = 1;
0077: public final static int UNKNOWN_LINE_ENDINGS = 2;
0078: public final static int BINARY = 3;
0079: public final static int DIRECTORY = 4;
0080:
0081: // Private instance variables
0082: private TextStyle style;
0083: private Project project;
0084: private FileStatus remoteStatus;
0085: private DirectoryStatus directoryStatus;
0086: private boolean directoryStatusChecked;
0087: private boolean needsAttention;
0088: private boolean selected, canPathChecked, expandedIcon,
0089: styleChecked;
0090: private String canPath;
0091: private boolean openChecked, open;
0092: private File file;
0093: private Vector children;
0094: private Vector allChildren;
0095: private CVSRoot cvsRoot;
0096: private Entry entry;
0097: private IgnoreFileFilter ignoreFilter;
0098: private int lineEndings;
0099: private CVSSubstType cvsSubstType;
0100: private boolean cvsSubstTypeChecked;
0101: private boolean cvsRootChecked;
0102: private MutableTreeNode parent;
0103: private Object userObject;
0104: private GruntspudContext context;
0105: private boolean module;
0106: private boolean moduleChecked;
0107: private boolean projectRoot;
0108: private Icon icon;
0109: private SortCriteria sortCriteria;
0110: private boolean includeAllFiles;
0111: private String fileTypeText;
0112: private HashMap formattedText;
0113: private boolean copy;
0114:
0115: /**
0116: * Constructor for the CVSFileNode object
0117: *
0118: *@param host application hosting gruntspud
0119: *@param filter file filter
0120: *@param entry the CVS entry (if any) associated with this node
0121: *@param file t
0122: */
0123: public CVSFileNode(GruntspudContext context,
0124: SortCriteria sortCriteria, Entry entry, File file,
0125: CVSFileNode parent) {
0126: this .context = context;
0127:
0128: formattedText = new HashMap();
0129:
0130: //
0131: selected = true;
0132: children = null;
0133: allChildren = null;
0134:
0135: //
0136: this .sortCriteria = sortCriteria;
0137: this .entry = entry;
0138: this .file = file;
0139: copy = file.getName().startsWith(".#");
0140: setParent(parent);
0141: context.getViewManager().cacheFileNode(this );
0142:
0143: initialize();
0144:
0145: }
0146:
0147: public Project getProject() {
0148: return project;
0149: }
0150:
0151: // This should be changed to a Comparator
0152: public static int compare(CVSFileNode t, CVSFileNode n,
0153: SortCriteria sortCriteria) {
0154: int s = 0;
0155:
0156: if (sortCriteria.isFoldersFirst() && t.isLeaf() && !n.isLeaf()) {
0157: s = 1;
0158: } else if (sortCriteria.isFoldersFirst() && n.isLeaf()
0159: && !t.isLeaf()) {
0160: s = -1;
0161: } else {
0162: switch (sortCriteria.getSortType()) {
0163: case CVSFileNode.SORT_ON_STATUS:
0164: s = new FileStatusWrapper(t.getOverallStatus())
0165: .compareTo(new FileStatusWrapper(n
0166: .getOverallStatus()));
0167: break;
0168: case CVSFileNode.SORT_ON_KEYWORD_SUBST:
0169: if ((t.getEntry() == null) && (n.getEntry() != null)) {
0170: s = -1;
0171: } else if ((t.getEntry() != null)
0172: && (n.getEntry() == null)) {
0173: s = 1;
0174: } else if ((t.getEntry() == null)
0175: && (n.getEntry() == null)) {
0176: s = 0;
0177: } else {
0178: s = t.getCVSSubstType().compareTo(
0179: n.getCVSSubstType());
0180: }
0181: break;
0182: case CVSFileNode.SORT_ON_NAME:
0183: String f1 = null;
0184: String f2 = null;
0185: if (t.getName().startsWith(".#")) {
0186: f1 = t.getFile().getParentFile().getAbsolutePath()
0187: + File.separator
0188: + t.getFile().getName().substring(2);
0189: } else {
0190: f1 = t.getFile().getAbsolutePath();
0191: }
0192: if (n.getName().startsWith(".#")) {
0193: f2 = n.getFile().getParentFile().getAbsolutePath()
0194: + File.separator
0195: + n.getFile().getName().substring(2);
0196: } else {
0197: f2 = n.getFile().getAbsolutePath();
0198: }
0199: if (sortCriteria.isCaseSensitive()) {
0200: s = f1.compareTo(f2);
0201: } else {
0202: s = f1.compareToIgnoreCase(f2);
0203: }
0204: break;
0205: case CVSFileNode.SORT_ON_TYPE:
0206: s = t.getFileTypeText().compareTo(n.getFileTypeText());
0207:
0208: break;
0209: case CVSFileNode.SORT_ON_REVISION:
0210: s = new Long(getLongRevision(t.getRevision()))
0211: .compareTo(new Long(getLongRevision(n
0212: .getRevision())));
0213:
0214: break;
0215: case CVSFileNode.SORT_ON_DATE:
0216:
0217: Date d1 = (t.getEntry() == null) ? null : t.getEntry()
0218: .getLastModified();
0219: Date d2 = (n.getEntry() == null) ? null : n.getEntry()
0220: .getLastModified();
0221:
0222: if ((d1 == null) && (d2 == null)) {
0223: s = 0;
0224: } else if ((d1 == null) && (d2 != null)) {
0225: s = -1;
0226: } else if ((d1 != null) && (d2 == null)) {
0227: s = 1;
0228: } else {
0229: s = d1.compareTo(d2);
0230:
0231: }
0232: break;
0233: case CVSFileNode.SORT_ON_FLAGS:
0234: s = t.getFlags().compareTo(n.getFlags());
0235:
0236: break;
0237: case CVSFileNode.SORT_ON_TAG:
0238: if ((t.getTag() == null) && (n.getTag() != null)) {
0239: s = -1;
0240: } else if ((t.getTag() != null) && (n.getTag() == null)) {
0241: s = 1;
0242: } else if ((t.getTag() == null) && (n.getTag() == null)) {
0243: s = 0;
0244: } else {
0245: s = t.getTag().compareTo(n.getTag());
0246: }
0247: break;
0248: case CVSFileNode.SORT_ON_LOCAL_STATUS:
0249: s = new FileStatusWrapper(t.getLocalStatus())
0250: .compareTo(new FileStatusWrapper(n
0251: .getLocalStatus()));
0252: break;
0253: case CVSFileNode.SORT_ON_REMOTE_STATUS:
0254: s = new FileStatusWrapper(t.getRemoteStatus())
0255: .compareTo(new FileStatusWrapper(n
0256: .getRemoteStatus()));
0257: break;
0258: case CVSFileNode.SORT_ON_SIZE:
0259:
0260: if (n.isLeaf()) {
0261: s = new Long(t.getFile().length())
0262: .compareTo(new Long(n.getFile().length()));
0263: } else {
0264: s = new Integer(n.getChildCount())
0265: .compareTo(new Integer(n.getChildCount()));
0266:
0267: }
0268: break;
0269: }
0270:
0271: // Reverse the sort if required
0272: if (sortCriteria.getSortDirection() == SortCriteria.SORT_ASCENDING) {
0273: s = s * -1;
0274: }
0275: }
0276:
0277: return s;
0278: }
0279:
0280: public String getCanonicalPath() {
0281: if (!canPathChecked) {
0282: try {
0283: canPath = getFile().getCanonicalPath();
0284: } catch (IOException ioe) {
0285: Constants.UI_LOG.error("Could not get canonical path");
0286: }
0287: canPathChecked = true;
0288: }
0289: return canPath;
0290: }
0291:
0292: /**
0293: * DOCUMENT ME!
0294: *
0295: * @param revision DOCUMENT ME!
0296: *
0297: * @return DOCUMENT ME!
0298: */
0299: public static long getLongRevision(String revision) {
0300: String[] s1 = StringUtil.splitString(revision, '.');
0301: int j = 1;
0302: long tot = 0;
0303:
0304: try {
0305: for (int i = s1.length - 1; i >= 0; i--) {
0306: int z = Integer.parseInt(s1[i]);
0307: tot = tot + (z * j);
0308: j = j * 100000; // surely there will never be a revision this large
0309: }
0310: } catch (NumberFormatException nfe) {
0311: }
0312:
0313: return tot;
0314: }
0315:
0316: /**
0317: * DOCUMENT ME!
0318: *
0319: * @param o DOCUMENT ME!
0320: *
0321: * @return DOCUMENT ME!
0322: */
0323: public int compareTo(Object o) {
0324: return compare(this , ((CVSFileNode) o), sortCriteria);
0325: }
0326:
0327: /**
0328: * Return the tag (may be blank)
0329: *
0330: * @return
0331: */
0332: public String getTag() {
0333: return (entry == null) ? "" : entry.getTag();
0334: }
0335:
0336: /**
0337: * Return the tag (may be blank)
0338: *
0339: * @return
0340: */
0341: public String getLocalStatusText() {
0342: return entry == null ? ""
0343: : (getLocalStatus() == null ? "Missing!"
0344: : getLocalStatus().toString());
0345: }
0346:
0347: /**
0348: * Return the tag (may be blank)
0349: *
0350: * @return
0351: */
0352: public String getOverallStatusText() {
0353: String s = getRemoteStatusText();
0354: return s.length() == 0 ? getLocalStatusText() : s;
0355: }
0356:
0357: /**
0358: * Return the tag (may be blank)
0359: *
0360: * @return
0361: */
0362: public String getRemoteStatusText() {
0363: return remoteStatus == null ? "Unknown" : remoteStatus
0364: .toString();
0365: }
0366:
0367: /**
0368: * Return the file type
0369: *
0370: * @return
0371: */
0372: public String getFileTypeText() {
0373: if (fileTypeText == null) {
0374: fileTypeText = CVSFileTypeUtil
0375: .getFileSystemTypeDescriptionName(getFile());
0376:
0377: }
0378: return (fileTypeText == null) ? "" : fileTypeText;
0379: }
0380:
0381: /**
0382: * Return the revision text (may be blank)
0383: *
0384: * @return revision text
0385: */
0386: public String getFlags() {
0387: return ((entry == null) || (entry.getOptions() == null)) ? ""
0388: : entry.getOptions();
0389: }
0390:
0391: /**
0392: * Return the revision text (may be blank)
0393: *
0394: * @return revision text
0395: */
0396: public String getRevision() {
0397: return (entry == null) ? "" : entry.getRevision();
0398: }
0399:
0400: /**
0401: * Does this node allow children (i.e. is it a directory)
0402: *
0403: *@return The allowsChildren value
0404: */
0405: public boolean getAllowsChildren() {
0406: return (entry == null) ? file.isDirectory() : entry
0407: .isDirectory();
0408: }
0409:
0410: /**
0411: * Give the index in the list of children for this for a specified node
0412: *
0413: *@param node the node to find
0414: *@return index of node in child list
0415: */
0416: public int getIndex(TreeNode node) {
0417: if (!isChildListLoaded()) {
0418: loadChildren();
0419: filterAndSortChildren();
0420: }
0421: return (children == null) ? (-1) : children.indexOf(node);
0422: }
0423:
0424: /**
0425: * Return an enumeration of all the children in this node (if any).
0426: *
0427: *@return enumeration of children
0428: */
0429: public Enumeration children() {
0430: if (!isChildListLoaded()) {
0431: loadChildren();
0432: filterAndSortChildren();
0433: }
0434: return children.elements();
0435: }
0436:
0437: /**
0438: * Add a new child node
0439: */
0440: public void addChild(CVSFileNode node) {
0441: if (!isChildListLoaded()) {
0442: loadChildren();
0443: filterAndSortChildren();
0444: }
0445: children.add(node);
0446: }
0447:
0448: /**
0449: * Adds <code>child</code> to the receiver at <code>index</code>. <code>child</code>
0450: * will be messaged with <code>setParent</code>.
0451: *
0452: *@param child Description of the Parameter
0453: *@param index Description of the Parameter
0454: */
0455: public void insert(MutableTreeNode child, int index) {
0456: if (children == null) {
0457: children = new Vector();
0458:
0459: }
0460: children.insertElementAt(child, index);
0461: }
0462:
0463: /**
0464: * Removes the child at <code>index</code> from the receiver.
0465: *
0466: *@param index Description of the Parameter
0467: */
0468: public void remove(int index) {
0469: CVSFileNode n = (CVSFileNode) getChildAt(index);
0470: if (n != null) {
0471: children.remove(n);
0472: allChildren.remove(n);
0473: }
0474: }
0475:
0476: /**
0477: * Removes <code>node</code> from the receiver. <code>setParent</code> will
0478: * be messaged on <code>node</code>.
0479: *
0480: *@param node Description of the Parameter
0481: */
0482: public void remove(MutableTreeNode node) {
0483: children.remove(node);
0484: allChildren.remove(node);
0485: }
0486:
0487: /**
0488: * Resets the user object of the receiver to <code>object</code>.
0489: *
0490: *@param object The new userObject value
0491: */
0492: public void setUserObject(Object object) {
0493: this .userObject = userObject;
0494: }
0495:
0496: /**
0497: * Removes the receiver from its parent.
0498: */
0499: public void removeFromParent() {
0500: if (getParent() != null) {
0501: ((CVSFileNode) getParent()).remove(this );
0502: }
0503: }
0504:
0505: /**
0506: * Sets the parent of the receiver to <code>newParent</code>.
0507: *
0508: *@param parent The new parent value
0509: */
0510: public void setParent(MutableTreeNode parent) {
0511: this .parent = parent;
0512: }
0513:
0514: /**
0515: * Sets the cVSFileType attribute of the CVSFileNode object
0516: *
0517: *@param cvsFileType The new cVSFileType value
0518: */
0519: public void setCVSSubstType(CVSSubstType cvsFileType) {
0520: this .cvsSubstType = cvsSubstType;
0521: cvsSubstTypeChecked = true;
0522: }
0523:
0524: /**
0525: * Gets the cVSFileType attribute of the CVSFileNode object
0526: *
0527: *@return The cVSFileType value
0528: */
0529: public CVSSubstType getCVSSubstType() {
0530: if (!cvsSubstTypeChecked) {
0531: if (isCopy()) {
0532: cvsSubstType = CVSSubstType.CVS_SUBST_TYPE_COPY;
0533: } else if (getParent() != null) {
0534: CVSFileNode p = (CVSFileNode) getParent();
0535:
0536: if ((p.getIgnoreFilter() != null)
0537: && (p.getFile() != null)) {
0538: boolean ignore = p.getIgnoreFilter()
0539: .shouldBeIgnored(p.getFile(), getName());
0540:
0541: if (ignore) {
0542: cvsSubstType = CVSSubstType.CVS_SUBST_TYPE_IGNORED;
0543: }
0544: }
0545: }
0546:
0547: if ((cvsSubstType == null) && (entry != null)) {
0548: if (isLeaf()) {
0549: if (entry.getOptions() == null) {
0550: cvsSubstType = CVSSubstType.CVS_SUBST_TYPE_TEXT;
0551: } else {
0552: cvsSubstType = CVSSubstType
0553: .getSubstTypeForString(entry
0554: .getOptions());
0555: }
0556: } else {
0557: cvsSubstType = CVSSubstType.CVS_SUBST_TYPE_DIRECTORY;
0558: }
0559: }
0560:
0561: cvsSubstTypeChecked = true;
0562: }
0563:
0564: return cvsSubstType;
0565: }
0566:
0567: /**
0568: * Description of the Method
0569: */
0570: public synchronized void reset() {
0571: if (file != null) {
0572: try {
0573: // if(entry != null && cvsRoot != null) {
0574: // String dir = CVSUtil.getRepositoryForDirectory(file.getParentFile());
0575: // Constants.CVS_LOG.debug("Updating admin data for " + entry.getName() + ". Parent is " +
0576: // file.getParent() + ", Repository=" + cvsRoot.getRepository() + " Repository For Directory =" + dir);
0577: // CVSUtil.getHandler().updateAdminData(
0578: // file.getParent(), dir, entry, CVSUtil.getGlobalOptions(context, null));
0579: // }
0580: entry = CVSUtil.getHandler().getEntry(file);
0581:
0582: } catch (IOException ioe) {
0583: Constants.IO_LOG.error(
0584: "Could not get CVS entry for file "
0585: + file.getAbsolutePath(), ioe);
0586: }
0587: }
0588: initialize();
0589: }
0590:
0591: private void initialize() {
0592: if (children != null) {
0593: context.getViewManager().removeCachedChildNodes(this );
0594: }
0595: style = null;
0596: icon = null;
0597: project = null;
0598: remoteStatus = null;
0599: needsAttention = false;
0600: selected = false;
0601: canPathChecked = false;
0602: expandedIcon = false;
0603: styleChecked = false;
0604: canPath = null;
0605: openChecked = false;
0606: open = false;
0607: children = null;
0608: allChildren = null;
0609: cvsRoot = null;
0610: ignoreFilter = null;
0611: cvsSubstType = null;
0612: cvsSubstTypeChecked = false;
0613: cvsRootChecked = false;
0614: module = false;
0615: moduleChecked = false;
0616: lineEndings = context.getHost().getBooleanProperty(
0617: Constants.OPTIONS_DISPLAY_SHOW_LINE_ENDINGS, false) ? LINE_ENDINGS_UNCHECKED
0618: : UNKNOWN_LINE_ENDINGS;
0619: directoryStatus = null;
0620: directoryStatusChecked = false;
0621: fileTypeText = null;
0622: formattedText.clear();
0623: project = null;
0624: projectRoot = false;
0625: if (parent != null) {
0626: project = ((CVSFileNode) parent).getProject();
0627: }
0628: if (project == null) {
0629: project = context.getProjectListModel().getProjectForFile(
0630: file);
0631: }
0632: try {
0633: if (project != null)
0634: projectRoot = project.getHome().getCanonicalFile()
0635: .equals(file.getCanonicalFile());
0636: } catch (IOException ioe) {
0637: Constants.SYSTEM_LOG.error("Could not determine if "
0638: + file.getAbsolutePath() + " is the project root.",
0639: ioe);
0640: }
0641: if (project != null
0642: && context.getViewManager().getProject() != null
0643: && context.getViewManager().getProject() == project) {
0644: filterAndSortChildren();
0645: }
0646: }
0647:
0648: public boolean isOpen() {
0649: if (!openChecked) {
0650: open = context.getHost().isNodeOpenedInEditor(this );
0651: openChecked = true;
0652: }
0653: return open;
0654: }
0655:
0656: public int getLineEndings() {
0657: if (lineEndings == LINE_ENDINGS_UNCHECKED) {
0658: lineEndings = CVSFileTypeUtil.getLineEndings(getFile());
0659: }
0660: return lineEndings;
0661: }
0662:
0663: /**
0664: * Description of the Method
0665: */
0666: public void loadIgnoreFilter() {
0667: if (!includeAllFiles && (ignoreFilter == null)
0668: && (file != null) && file.isDirectory()) {
0669: ignoreFilter = CVSUtil.getCompleteIgnoreFileFilter(context,
0670: file);
0671: }
0672: }
0673:
0674: /**
0675: * Gets the ignored attribute of the CVSFileNode object
0676: *
0677: *@return The ignored value
0678: */
0679: public boolean isIgnored() {
0680: return CVSSubstType.CVS_SUBST_TYPE_IGNORED == getCVSSubstType();
0681: }
0682:
0683: /**
0684: * Gets the ignoreFilter attribute of the CVSFileNode object
0685: *
0686: *@return The ignoreFilter value
0687: */
0688: public IgnoreFileFilter getIgnoreFilter() {
0689: if (ignoreFilter == null) {
0690: loadIgnoreFilter();
0691:
0692: }
0693: return ignoreFilter;
0694: }
0695:
0696: /**
0697: * Gets the foreground attribute of the CVSFileNode object
0698: *
0699: *@return The foreground value
0700: */
0701: public Color getForeground() {
0702: return ((getEntry() != null) && !getFile().canRead()) ? Color.red
0703: : null;
0704: }
0705:
0706: public TextStyle getStyle() {
0707: if (!styleChecked) {
0708: if (getEntry() == null && !getFile().canRead()) {
0709: style = context.getTextStyleModel().getStyle(
0710: "file.Unreadable");
0711: } else {
0712: if (!isLeaf()) {
0713: if (isNeedsAttention()) {
0714: style = context.getTextStyleModel().getStyle(
0715: "folder.RequiresAttention");
0716: } else if (isProjectRoot()) {
0717: style = context.getTextStyleModel().getStyle(
0718: "folder.Project");
0719: } else if (isModuleRoot()) {
0720: style = context.getTextStyleModel().getStyle(
0721: "folder.Module");
0722: } else {
0723: style = context.getTextStyleModel().getStyle(
0724: "folder.Plain");
0725: }
0726: }
0727: }
0728: if (style == null)
0729: style = context.getHost().getNodeStyle(this );
0730: styleChecked = true;
0731: }
0732: return style;
0733: }
0734:
0735: /**
0736: * Gets the icon attribute of the CVSFileNode object
0737: *
0738: *@param expanded Description of the Parameter
0739: *@return The icon value
0740: */
0741: public Icon getIcon(boolean expanded) {
0742: if (expanded != expandedIcon) {
0743: icon = null;
0744: }
0745: if (icon == null) {
0746: icon = getIconForStatus(getOverallStatus(),
0747: getBaseIcon(expanded));
0748: expandedIcon = expanded;
0749: }
0750:
0751: // Unknown
0752: return icon;
0753: }
0754:
0755: public Icon getIconForStatus(FileStatus status, Icon base) {
0756:
0757: if (isNeedsAttention()) {
0758: return new OverlayIcon(
0759: UIUtil
0760: .getCachedIcon(Constants.ICON_STATUS_NEEDS_ATTENTION),
0761: base, SwingConstants.CENTER);
0762: } else if (isProjectRoot()) {
0763: return new OverlayIcon(
0764: UIUtil
0765: .getCachedIcon(Constants.ICON_STATUS_PROJECT_OVERLAY),
0766: base, SwingConstants.CENTER);
0767: } else if (isModuleRoot()) {
0768: return new OverlayIcon(
0769: UIUtil
0770: .getCachedIcon(Constants.ICON_STATUS_MODULE_OVERLAY),
0771: base, SwingConstants.CENTER);
0772: } else {
0773: return CVSFileTypeUtil.getIconForStatus(status, base);
0774: }
0775:
0776: }
0777:
0778: public Icon getBaseIcon(boolean expanded) {
0779: boolean useSystemIcons = context.getHost().getBooleanProperty(
0780: Constants.OPTIONS_DISPLAY_USE_SYSTEM_ICONS, true);
0781: if ((file != null) && file.exists()) {
0782: return file.isDirectory() ? (expanded ? context
0783: .getHost()
0784: .getIcon(
0785: Constants.ICON_TOOL_SMALL_DEFAULT_FOLDER_OPEN)
0786: : context
0787: .getHost()
0788: .getIcon(
0789: Constants.ICON_TOOL_SMALL_DEFAULT_FOLDER_CLOSED))
0790: : (useSystemIcons ? CVSFileTypeUtil
0791: .getFileSystemIcon(file)
0792: : (isOpen() ? context
0793: .getHost()
0794: .getIcon(
0795: Constants.ICON_TOOL_SMALL_DEFAULT_LEAF_OPEN)
0796: : context
0797: .getHost()
0798: .getIcon(
0799: Constants.ICON_TOOL_SMALL_DEFAULT_LEAF)));
0800: } else {
0801: return isLeaf() ? (isOpen() ? context.getHost().getIcon(
0802: Constants.ICON_TOOL_SMALL_DEFAULT_LEAF_OPEN)
0803: : context.getHost().getIcon(
0804: Constants.ICON_TOOL_SMALL_DEFAULT_LEAF))
0805: : (expanded ? context
0806: .getHost()
0807: .getIcon(
0808: Constants.ICON_TOOL_SMALL_DEFAULT_FOLDER_OPEN)
0809: : context
0810: .getHost()
0811: .getIcon(
0812: Constants.ICON_TOOL_SMALL_DEFAULT_FOLDER_CLOSED));
0813: }
0814:
0815: }
0816:
0817: /**
0818: * Gets the binary attribute of the CVSFileNode object
0819: *
0820: *@return The binary value
0821: */
0822: public boolean isBinary() {
0823: return CVSSubstType.CVS_SUBST_TYPE_BINARY == getCVSSubstType();
0824: }
0825:
0826: /**
0827: * Gets the text attribute of the CVSFileNode object
0828: *
0829: *@return The text value
0830: */
0831: public boolean isText() {
0832: return CVSSubstType.CVS_SUBST_TYPE_IGNORED == getCVSSubstType();
0833: }
0834:
0835: /**
0836: * Gets the unicode attribute of the CVSFileNode object
0837: *
0838: *@return The unicode value
0839: */
0840: public boolean isUnicode() {
0841: return CVSSubstType.CVS_SUBST_TYPE_IGNORED == getCVSSubstType();
0842: }
0843:
0844: /**
0845: * Gets the name attribute of the CVSFileNode object
0846: *
0847: *@return The name value
0848: */
0849: public String getName() {
0850: return (file == null) ? entry.getName() : file.getName();
0851: }
0852:
0853: /**
0854: * Gets the name attribute of the CVSFileNode object
0855: *
0856: *@return The name value
0857: */
0858: public String getFormattedText(String format) {
0859: // If no formatting
0860: if ((format == null) || !isLeaf()) {
0861: return getName();
0862: }
0863:
0864: // Otherwise parse the pattern
0865: String ft = (String) formattedText.get(format);
0866: if (ft == null) {
0867: int l = format.length();
0868: StringBuffer buf = new StringBuffer(20);
0869: char ch = ' ';
0870:
0871: for (int i = 0; i < l; i++) {
0872: ch = format.charAt(i);
0873:
0874: if ((ch == '%') && ((i + 1) < l)) {
0875: i++;
0876: ch = format.charAt(i);
0877:
0878: switch (ch) {
0879: case 'n':
0880: buf.append(getName());
0881:
0882: break;
0883: case 'r':
0884: buf.append(getRevision());
0885:
0886: break;
0887: case 's':
0888: buf.append(getOverallStatusText());
0889: break;
0890: case 'l':
0891: buf.append(getLocalStatusText());
0892: break;
0893: case 'R':
0894: buf.append(getRemoteStatusText());
0895: break;
0896: case 'P':
0897: buf.append("..");
0898:
0899: CVSFileNode n = context.getViewManager()
0900: .getCWDNode();
0901:
0902: if (n != null) {
0903: String nf = n.getFile().getAbsolutePath();
0904: String f = getFile().getAbsolutePath();
0905:
0906: if (nf.length() < f.length()) {
0907: buf.append(f.substring(nf.length()));
0908: } else {
0909: buf.append(f);
0910: }
0911: }
0912:
0913: break;
0914: case 'p':
0915: buf.append(getFile().getAbsolutePath());
0916:
0917: break;
0918: default:
0919: buf.append(ch);
0920: }
0921: } else {
0922: buf.append(ch);
0923: }
0924: }
0925:
0926: ft = buf.toString();
0927: formattedText.put(format, ft);
0928: }
0929:
0930: return ft;
0931: }
0932:
0933: /**
0934: * Gets the entry attribute of the CVSFileNode object
0935: *
0936: *@return The entry value
0937: */
0938: public Entry getEntry() {
0939: return entry;
0940: }
0941:
0942: /**
0943: * Gets the parent attribute of the CVSFileNode object
0944: *
0945: *@return The parent value
0946: */
0947: public TreeNode getParent() {
0948: return parent;
0949: }
0950:
0951: /**
0952: * Gets the cVSRoot attribute of the CVSFileNode object
0953: *
0954: *@return The cVSRoot value
0955: */
0956: public CVSRoot getCVSRoot() {
0957: if (!cvsRootChecked) {
0958: if (getFile() != null) {
0959:
0960: cvsRoot = CVSUtil.getCVSRoot(getFile(), context);
0961:
0962: if ((cvsRoot == null) && (getParent() != null)) {
0963: cvsRoot = ((CVSFileNode) getParent()).getCVSRoot();
0964: }
0965: } else if (getParent() != null) {
0966: cvsRoot = ((CVSFileNode) getParent()).getCVSRoot();
0967:
0968: }
0969: cvsRootChecked = true;
0970: }
0971:
0972: return cvsRoot;
0973: }
0974:
0975: /**
0976: * Gets the moduleRoot attribute of the CVSFileNode object
0977: *
0978: *@return The moduleRoot value
0979: */
0980: public boolean isProjectRoot() {
0981: return projectRoot;
0982: }
0983:
0984: /**
0985: * Gets the moduleRoot attribute of the CVSFileNode object
0986: *
0987: *@return The moduleRoot value
0988: */
0989: public boolean isModuleRoot() {
0990: if (!moduleChecked) {
0991: if (getFile().isFile() || !getFile().exists()) {
0992: module = false;
0993: } else {
0994: String repository = CVSUtil
0995: .getRepositoryForDirectory(getFile());
0996:
0997: // May be checked-out under a different name
0998: // module = (repository != null) &&
0999: // repository.equals(getFile().getName());
1000:
1001: module = (repository != null)
1002: && repository.length() > 0
1003: && repository.indexOf('/') == -1;
1004: }
1005:
1006: moduleChecked = true;
1007: }
1008:
1009: return module;
1010: }
1011:
1012: /**
1013: * Gets the remote status
1014: *
1015: *@return remote status
1016: */
1017: public FileStatus getRemoteStatus() {
1018: return remoteStatus;
1019: }
1020:
1021: /**
1022: * Sets the remote status
1023: *
1024: *@return remoteStatus remote status
1025: */
1026: public void setRemoteStatus(FileStatus remoteStatus) {
1027: this .remoteStatus = remoteStatus;
1028: }
1029:
1030: /**
1031: * Gets the entry attribute of the CVSFileNode object
1032: *
1033: *@param name Description of the Parameter
1034: *@return The entry value
1035: *@exception IOException Description of the Exception
1036: */
1037: public Entry getEntry(String name) throws IOException {
1038: for (Iterator i = children.iterator(); i.hasNext();) {
1039: CVSFileNode n = (CVSFileNode) i.next();
1040:
1041: if ((n.getEntry() != null)
1042: && n.getEntry().getName().equals(name)) {
1043: return n.getEntry();
1044: }
1045: }
1046:
1047: return null;
1048: }
1049:
1050: /**
1051: * Gets the leaf attribute of the CVSFileNode object
1052: *
1053: *@return The leaf value
1054: */
1055: public boolean isLeaf() {
1056: return (entry == null) ? (!getFile().isDirectory()) : (!entry
1057: .isDirectory());
1058: }
1059:
1060: /**
1061: * Gets the childCount attribute of the CVSFileNode object
1062: *
1063: *@return The childCount value
1064: */
1065: public int getChildCount() {
1066: if (!isChildListLoaded()) {
1067: loadChildren();
1068: filterAndSortChildren();
1069: }
1070: return isLeaf() ? 0
1071: : ((children == null) ? 1 : children.size());
1072: }
1073:
1074: public int getUnfilteredChildCount() {
1075: if (!isChildListLoaded()) {
1076: loadChildren();
1077: filterAndSortChildren();
1078: }
1079: return isLeaf() ? 0 : ((allChildren == null) ? 1 : allChildren
1080: .size());
1081: }
1082:
1083: /**
1084: * Description of the Method
1085: */
1086: public synchronized void loadChildren() {
1087: // if(Constants.SYSTEM_LOG.isDebugEnabled())
1088: // Constants.SYSTEM_LOG.debug("Loading children for " + getName());
1089: // if ( (allChildren == null) && (file != null) && file.isDirectory()) {
1090: // allChildren = new Vector();
1091: // children = new Vector();
1092: allChildren = new Vector();
1093: children = new Vector();
1094:
1095: try {
1096: File[] f = getFile().listFiles();
1097:
1098: if (f != null) {
1099: for (int i = 0; i < f.length && !Thread.interrupted()
1100: && !context.getViewManager().isStopTreeLoad(); i++) {
1101: Entry entry = CVSUtil.getHandler().getEntry(f[i]);
1102: CVSFileNode n = new CVSFileNode(context,
1103: sortCriteria, entry, f[i], this );
1104: n.setIncludeAllFiles(includeAllFiles);
1105: allChildren.addElement(n);
1106: }
1107: }
1108:
1109: for (Iterator i = CVSUtil.getHandler()
1110: .getEntries(getFile()); i.hasNext()
1111: && !Thread.interrupted()
1112: && !context.getViewManager().isStopTreeLoad();) {
1113: Entry e = (Entry) i.next();
1114: boolean found = false;
1115:
1116: for (int j = 0; (j < allChildren.size()) && !found; j++) {
1117: if (((CVSFileNode) allChildren.elementAt(j))
1118: .getName().equals(e.getName())) {
1119: found = true;
1120: }
1121: }
1122:
1123: if (!found) {
1124: CVSFileNode n = new CVSFileNode(context,
1125: sortCriteria, e, new File(getFile(), e
1126: .getName()), this );
1127: n.setIncludeAllFiles(includeAllFiles);
1128: allChildren.addElement(n);
1129: }
1130: }
1131: } catch (IOException ioe) {
1132: ioe.printStackTrace();
1133: }
1134: }
1135:
1136: public boolean isNeedsAttention() {
1137: return needsAttention;
1138: }
1139:
1140: public synchronized void filterAndSortChildren() {
1141: if (!isLeaf() && !isChildListLoaded() && project != null
1142: && context.getViewManager().getProject() == project)
1143: loadChildren();
1144: if (!isLeaf() && !isChildListLoaded()) {
1145: return;
1146: }
1147: needsAttention = false;
1148: directoryStatus = null;
1149: directoryStatusChecked = false;
1150: icon = null;
1151: style = null;
1152: styleChecked = false;
1153: CVSFileFilter filter = context.getFilterModel()
1154: .getSelectedFilter();
1155: CVSFileNode n = null;
1156: FileStatus s = null;
1157: if (!isLeaf()) {
1158: children.removeAllElements();
1159: for (Iterator i = allChildren.iterator(); i.hasNext()
1160: && !context.getViewManager().isStopTreeLoad();) {
1161: n = (CVSFileNode) i.next();
1162: if (n.isLeaf()) {
1163: if (includeAllFiles || (filter == null)
1164: || filter.acceptNode(n)) {
1165: n.setParent(this );
1166: if (!needsAttention && project != null) {
1167: s = n.getOverallStatus();
1168: if (FileStatus.UP_TO_DATE != s) {
1169: needsAttention = true;
1170: }
1171: }
1172: children.addElement(n);
1173: }
1174: } else {
1175: if (!n.isIgnored()) {
1176: n.filterAndSortChildren();
1177: if (!needsAttention) {
1178: needsAttention = n.isNeedsAttention();
1179: }
1180: if (includeAllFiles || (filter == null)
1181: || (project == null)
1182: || filter.acceptNode(n)) {
1183: children.addElement(n);
1184: }
1185: }
1186: }
1187: }
1188: Collections.sort(children);
1189: }
1190: }
1191:
1192: /**
1193: * Gets the childListLoaded attribute of the CVSFileNode object
1194: *
1195: *@return The childListLoaded value
1196: */
1197: public boolean isChildListLoaded() {
1198: return (children == null || allChildren == null) ? false : true;
1199: }
1200:
1201: /**
1202: * Gets the childAt attribute of the CVSFileNode object
1203: *
1204: *@param i Description of the Parameter
1205: *@return The childAt value
1206: */
1207: public TreeNode getChildAt(int i) {
1208: if (!isChildListLoaded()) {
1209: loadChildren();
1210: filterAndSortChildren();
1211: }
1212:
1213: if (children == null) {
1214: return null;
1215: } else {
1216: return (TreeNode) children.elementAt(i);
1217: }
1218: }
1219:
1220: /**
1221: * Gets the childAt attribute of the CVSFileNode object
1222: *
1223: *@param i Description of the Parameter
1224: *@return The childAt value
1225: */
1226: public TreeNode getUnfilteredChildAt(int i) {
1227: if (!isChildListLoaded()) {
1228: loadChildren();
1229: filterAndSortChildren();
1230: }
1231:
1232: if (allChildren == null) {
1233: return null;
1234: } else {
1235: return (TreeNode) allChildren.elementAt(i);
1236: }
1237: }
1238:
1239: /**
1240: * Sets the selected attribute of the CVSFileNode object
1241: *
1242: *@param selected The new selected value
1243: */
1244: public void setSelected(boolean selected) {
1245: this .selected = selected;
1246: }
1247:
1248: /**
1249: * Gets the file attribute of the CVSFileNode object
1250: *
1251: *@return The file value
1252: */
1253: public File getFile() {
1254: return file;
1255: }
1256:
1257: /**
1258: * Gets the selected attribute of the CVSFileNode object
1259: *
1260: *@return The selected value
1261: */
1262: public boolean isSelected() {
1263: return selected;
1264: }
1265:
1266: /**
1267: * Return if this node is a copy
1268: *
1269: * @return copy
1270: */
1271: public boolean isCopy() {
1272: return copy;
1273: }
1274:
1275: /**
1276: * DOCUMENT ME!
1277: *
1278: * @param v DOCUMENT ME!
1279: * @param includeDir DOCUMENT ME!
1280: * @param singleCopy DOCUMENT ME!
1281: */
1282: public void recurseNodes(Vector v, boolean includeDir,
1283: boolean singleCopy) {
1284: recurseNodes(this , v, includeDir, singleCopy);
1285: }
1286:
1287: private void recurseNodes(CVSFileNode node, Vector v,
1288: boolean includeDir, boolean singleCopy) {
1289: if (node.isLeaf()) {
1290: if (!singleCopy || (singleCopy && (v.indexOf(node) == -1))) {
1291: v.addElement(node);
1292: }
1293: } else {
1294: for (int i = 0; i < node.getChildCount(); i++) {
1295: CVSFileNode n = (CVSFileNode) node.getChildAt(i);
1296:
1297: if (n != null) {
1298: recurseNodes(n, v, includeDir, singleCopy);
1299: }
1300: }
1301:
1302: if (includeDir) {
1303: v.addElement(node);
1304: }
1305: }
1306: }
1307:
1308: /**
1309: * Return the overall status for this node. Remote status takes precedence
1310: */
1311: public FileStatus getOverallStatus() {
1312: return getRemoteStatus() == null ? getLocalStatus()
1313: : getRemoteStatus();
1314: }
1315:
1316: public DirectoryStatus getDirectoryStatus() {
1317: if (!directoryStatusChecked) {
1318: if (isNeedsAttention()) {
1319: directoryStatus = DirectoryStatus.ATTENTION;
1320: } else if (isProjectRoot()) {
1321: directoryStatus = DirectoryStatus.PROJECT;
1322: } else if (isModuleRoot()) {
1323: directoryStatus = DirectoryStatus.MODULE;
1324: } else if (!isLeaf()) {
1325: if (getLocalStatus() == FileStatus.UP_TO_DATE)
1326: directoryStatus = DirectoryStatus.UP_TO_DATE;
1327: else
1328: directoryStatus = DirectoryStatus.PLAIN;
1329: } else {
1330: directoryStatus = null;
1331: }
1332: directoryStatusChecked = true;
1333: }
1334: return directoryStatus;
1335: }
1336:
1337: /**
1338: * Return the file status for this node
1339: */
1340: public FileStatus getLocalStatus() {
1341: if (entry == null) {
1342: return FileStatus.UNKNOWN;
1343: }
1344:
1345: File file = getFile();
1346:
1347: if ((entry != null) && entry.isNewUserFile()) {
1348: return FileStatus.ADDED;
1349: }
1350:
1351: if ((entry != null) && entry.isUserFileToBeRemoved()) {
1352: return FileStatus.REMOVED;
1353: }
1354:
1355: if ((entry != null) && (file != null) && !file.exists()) {
1356: return null;
1357: }
1358:
1359: if ((entry != null)
1360: && (file != null)
1361: && ((entry.getLastModified() != null)
1362: && !DateComparator.getInstance().equals(
1363: file.lastModified(),
1364: entry.getLastModified().getTime()) || (entry != null
1365: && entry.getLastModified() == null && "Result of merge"
1366: .equals(entry.getConflict())))) {
1367: return FileStatus.MODIFIED;
1368: }
1369:
1370: if ((entry != null) && entry.hadConflicts()) {
1371: return FileStatus.HAS_CONFLICTS;
1372: }
1373:
1374: return FileStatus.UP_TO_DATE;
1375: }
1376:
1377: /**
1378: * DOCUMENT ME!
1379: *
1380: * @param includeAllFiles DOCUMENT ME!
1381: */
1382: public void setIncludeAllFiles(boolean includeAllFiles) {
1383: this .includeAllFiles = includeAllFiles;
1384: }
1385:
1386: /**
1387: * DOCUMENT ME!
1388: *
1389: * @return DOCUMENT ME!
1390: */
1391: public String toString() {
1392: return getName();
1393: }
1394:
1395: static class FileStatusWrapper implements Comparable {
1396:
1397: FileStatus status;
1398:
1399: public FileStatusWrapper(FileStatus status) {
1400: this .status = status;
1401: }
1402:
1403: public int getImportance() {
1404: if (FileStatus.HAS_CONFLICTS == status) {
1405: return 90;
1406: }
1407: if (FileStatus.REMOVED == status) {
1408: return 80;
1409: }
1410: if (FileStatus.ADDED == status) {
1411: return 70;
1412: }
1413: if (FileStatus.MODIFIED == status) {
1414: return 60;
1415: }
1416: if (FileStatus.NEEDS_CHECKOUT == status) {
1417: return 50;
1418: }
1419: if (FileStatus.NEEDS_MERGE == status) {
1420: return 40;
1421: }
1422: if (FileStatus.NEEDS_PATCH == status) {
1423: return 30;
1424: }
1425: if (FileStatus.UP_TO_DATE == status) {
1426: return 20;
1427: }
1428: if (FileStatus.UNKNOWN == status) {
1429: return 10;
1430: }
1431: return 0;
1432: }
1433:
1434: public int compareTo(Object o) {
1435: if (o == null) {
1436: return 100;
1437: } else {
1438: FileStatusWrapper s = (FileStatusWrapper) o;
1439: int i1 = getImportance();
1440: int i2 = s.getImportance();
1441: return i1 - i2;
1442: }
1443: }
1444: }
1445: }
|