/*
 * ====================================================================
 * Copyright (c) 2004-2012 TMate Software Ltd.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://svnkit.com/license.html
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 * ====================================================================
 */
package org.tmatesoft.svn.core.wc.xml;

import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

import org.tmatesoft.svn.core.ISVNDirEntryHandler;
import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLock;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.util.SVNDate;
import org.tmatesoft.svn.util.ISVNDebugLog;
import org.tmatesoft.svn.util.SVNLogType;

import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;


/**
 * This is an implementation of the <b>ISVNStatusHandler</b> interface 
 * that writes XML formatted status information to a specified 
 * <b>ContentHandler</b>. 
 *  
 * @version 1.3
 * @author  TMate Software Ltd.
 * @since   1.2
 */
public class SVNXMLDirEntryHandler extends AbstractXMLHandler implements ISVNDirEntryHandler, Comparator {
    /**
     * <code>'expires'</code> tag.
     */
    public static final String EXPIRES_TAG = "expires";
    
    /**
     * <code>'created'</code> tag.
     */
    public static final String CREATED_TAG = "created";

    /**
     * <code>'comment'</code> tag.
     */
    public static final String COMMENT_TAG = "comment";
    
    /**
     * <code>'owner'</code> tag.
     */
    public static final String OWNER_TAG = "owner";

    /**
     * <code>'tag'</code> tag.
     */
    public static final String TOKEN_TAG = "token";

    /**
     * <code>'lock'</code> tag.
     */
    public static final String LOCK_TAG = "lock";

    /**
     * <code>'path'</code> attribute.
     */
    public static final String PATH_ATTR = "path";

    /**
     * <code>'revision'</code> attribute.
     */
    public static final String REVISION_ATTR = "revision";
    
    /**
     * <code>'lists'</code> tag.
     */
    public static final String LISTS_TAG = "lists";

    /**
     * <code>'list'</code> tag.
     */
    public static final String LIST_TAG = "list";

    /**
     * <code>'entry'</code> tag.
     */
    public static final String ENTRY_TAG = "entry";

    /**
     * <code>'name'</code> tag.
     */
    public static final String NAME_TAG = "name";

    /**
     * <code>'size'</code> tag.
     */
    public static final String SIZE_TAG = "size";

    /**
     * <code>'commit'</code> tag.
     */
    public static final String COMMIT_TAG = "commit";

    /**
     * <code>'date'</code> tag.
     */
    public static final String DATE_TAG = "date";

    /**
     * <code>'author'</code> tag.
     */
    public static final String AUTHOR_TAG = "author";

    private Collection myDirEntries;
    
    /**
     * Creates a new handler.
     * 
     * @param saxHandler a <b>ContentHandler</b> to form 
     *                   an XML tree
     */
    public SVNXMLDirEntryHandler(ContentHandler saxHandler) {
        this(saxHandler, null);
    }    

    /**
     * Creates a new handler.
     * 
     * @param saxHandler a <b>ContentHandler</b> to form 
     *                   an XML tree
     * @param log        a debug logger
     */
    public SVNXMLDirEntryHandler(ContentHandler saxHandler, ISVNDebugLog log) {
        super(saxHandler, log);
    }    
    
    /**
     * Begins an XML tree with the target path for which the 
     * status is run. 
     * 
     * @param path a WC target path or URL
     */
    public void startTarget(String path) {
        myDirEntries = new TreeSet(this);
        try {
            addAttribute(PATH_ATTR, path == null || path.length() == 9 ? "." : path);
            openTag(LIST_TAG);
        } catch (SAXException e) {
            getDebugLog().logSevere(SVNLogType.DEFAULT, e);
        }
    }

    /**
     * Handles a next dir <code>entry</code>.
     * 
     * @param  entry        dir entry 
     * @throws SVNException 
     */
    public void handleDirEntry(SVNDirEntry entry) throws SVNException {
        myDirEntries.add(entry);
    }
    
    /**
     * Closes the formatted XML output. 
     *
     */
    public void endTarget() {
        try {
            for (Iterator ents = myDirEntries.iterator(); ents.hasNext();) {
                SVNDirEntry entry = (SVNDirEntry) ents.next();
                sendToHandler(entry);
            }
            myDirEntries = null;
            closeTag(LIST_TAG);
        } catch (SAXException e) {
            getDebugLog().logSevere(SVNLogType.DEFAULT, e);
        }
    }
    
    private void sendToHandler(SVNDirEntry entry) throws SAXException {
        if ("".equals(entry.getRelativePath())) {
            if (entry.getKind() == SVNNodeKind.DIR) {
                return;
            }
        }
        openTag(ENTRY_TAG);
        addTag(NAME_TAG, entry.getRelativePath());
        if (entry.getKind() == SVNNodeKind.FILE) {
            addTag(SIZE_TAG, entry.getSize() + "");
        }
        
        addAttribute(REVISION_ATTR, entry.getRevision() + "");
        openTag(COMMIT_TAG);
        addTag(AUTHOR_TAG, entry.getAuthor());
        addTag(DATE_TAG, SVNDate.formatDate(entry.getDate()));
        closeTag(COMMIT_TAG);
        SVNLock lock = entry.getLock();
        if (lock != null) {
            openTag(LOCK_TAG);
            addTag(TOKEN_TAG, lock.getID());
            addTag(OWNER_TAG, lock.getOwner());
            addTag(COMMENT_TAG, lock.getComment());
            addTag(CREATED_TAG, SVNDate.formatDate(lock.getCreationDate()));
            if (lock.getExpirationDate() != null && lock.getExpirationDate().getTime() > 0) {
                addTag(EXPIRES_TAG, SVNDate.formatDate(lock.getExpirationDate()));
            }
            closeTag(LOCK_TAG);
        }
        closeTag(ENTRY_TAG);
    }
    
    protected String getHeaderName() {
        return LISTS_TAG;
    }
    
    /**
     * Compares two objects.
     * 
     * @param  o1 the first object to compare
     * @param  o2 the second object to compare
     * @return    0 if objects are equal; -1 if <code>o1</code> is 
     *            <span class="javakeyword">null</span> or if both 
     *            <code>o1</code> and <code>o2</code> are <b>SVNDirEntry</b> 
     *            objects and the relative path of the first object is 
     *            lexicographically less than that of the second one; 1 otherwise 
     */
    public int compare(Object o1, Object o2) {
        if (o1 == o2) {
            return 0;
        }
        SVNDirEntry e1 = (SVNDirEntry) o1;
        SVNDirEntry e2 = (SVNDirEntry) o2;
        if (e1 == null) {
            return -1;
        } else if (e2 == null) {
            return 1;
        }
        return SVNPathUtil.PATH_COMPARATOR.compare(e1.getRelativePath(), e2.getRelativePath());
    }
}
