SVNReader.java :  » Source-Control » tmatesoft-SVN » org » tmatesoft » svn » core » internal » io » svn » Java Open Source

Java Open Source » Source Control » tmatesoft SVN 
tmatesoft SVN » org » tmatesoft » svn » core » internal » io » svn » SVNReader.java
/*
 * ====================================================================
 * Copyright (c) 2004-2008 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.internal.io.svn;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.tmatesoft.svn.core.ISVNDirEntryHandler;
import org.tmatesoft.svn.core.SVNAuthenticationException;
import org.tmatesoft.svn.core.SVNCancelException;
import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
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.SVNTimeUtil;
import org.tmatesoft.svn.core.internal.wc.SVNCancellableOutputStream;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.util.SVNDebugLog;

/**
 * @version 1.1.1
 * @author  TMate Software Ltd.
 */
class SVNReader {

    public static Date getDate(Object[] items, int index) {
        String str = getString(items, index);
        return SVNTimeUtil.parseDate(str);
    }

    public static long getLong(Object[] items, int index) {
        if (items == null || index >= items.length) {
            return -1;
        }
        if (items[index] instanceof Long) {
            return ((Long) items[index]).longValue();
        } else if (items[index] instanceof Integer) {
            return ((Integer) items[index]).intValue();
        }
        return -1;
    }

    public static boolean getBoolean(Object[] items, int index) {
        if (items == null || index >= items.length) {
            return false;
        }
        if (items[index] instanceof Boolean) {
            return ((Boolean) items[index]).booleanValue();
        } else if (items[index] instanceof String) {
            return Boolean.valueOf((String) items[index]).booleanValue();
        }
        return false;

    }

    public static Map getMap(Object[] items, int index) {
        if (items == null || index >= items.length) {
            return Collections.EMPTY_MAP;
        }
        if (items[index] instanceof Map) {
            return (Map) items[index];
        }
        return Collections.EMPTY_MAP;
    }

    public static List getList(Object[] items, int index) {
        if (items == null || index >= items.length) {
            return Collections.EMPTY_LIST;
        }
        if (items[index] instanceof List) {
            return (List) items[index];
        }
        return Collections.EMPTY_LIST;
    }

    public static String getString(Object[] items, int index) {
        if (items == null || index >= items.length) {
            return null;
        }
        if (items[index] instanceof byte[]) {
            try {
                return new String((byte[]) items[index], "UTF-8");
            } catch (IOException e) {
                return null;
            }
        } else if (items[index] instanceof String) {
            return (String) items[index];
        }
        return null;
    }

    public static boolean hasValue(Object[] items, int index, boolean value) {
        return hasValue(items, index, Boolean.valueOf(value));
    }

    public static boolean hasValue(Object[] items, int index, int value) {
        return hasValue(items, index, new Long(value));
    }

    public static boolean hasValue(Object[] items, int index, Object value) {
        if (items == null || index >= items.length) {
            return false;
        }
        if (items[index] instanceof List) {
            // look in list.
            for (Iterator iter = ((List) items[index]).iterator(); iter
                    .hasNext();) {
                Object element = iter.next();
                if (element.equals(value)) {
                    return true;
                }
            }
        } else {
            if (items[index] == null) {
                return value == null;
            }
            if (items[index] instanceof byte[] && value instanceof String) {
                try {
                    items[index] = new String((byte[]) items[index], "UTF-8");
                } catch (IOException e) {
                    return false;
                }
            }
            return items[index].equals(value);
        }
        return false;
    }

    /**
     * upper case - read or delegate lower case - skip
     * 
     * 's' - read string as string or delegate 'w' - read word as string or
     * delegate 't' - read word as Boolean 'b' - read string as byte[] or
     * delegate 'i' - push input to passed output stream 'n' - read number as
     * Integer 'p' - properties map entry (name => value) 'l' - lock description
     * 
     * 'd' - dir entry (get-dir svn command response) 'f' - stat command
     * responce 'e' - edit command
     * 
     * '(' and ')' - list brackets '[' and ']' - command response, check for
     * 'failure', equals to '(w?(*e))' where w = success | failure
     * 
     * '?' - 0..1 tokens '*' - 0..* tokens cardinality only applicable for
     * tokens, not for groups.
     * 
     */

    public static Object[] parse(InputStream is, String templateStr, Object[] target) throws SVNException {
        if (target != null) {
            for (int i = 0; i < target.length; i++) {
                if (target[i] instanceof Collection || target[i] instanceof Map
                        || target[i] instanceof ISVNDirEntryHandler
                        || target[i] instanceof ISVNEditor
                        || target[i] instanceof OutputStream) {
                    continue;
                }
                target[i] = null;
            }
        }
        StringBuffer template = normalizeTemplate(templateStr);
        SVNEditModeReader editorBaton = null;
        int targetIndex = 0;
        boolean unconditionalThrow = false;
        for (int i = 0; i < template.length(); i++) {
            char ch = template.charAt(i);
            boolean optional = ch == '?' || ch == '*';
            boolean multiple = ch == '*';
            boolean doRead = Character.isUpperCase(ch)
                    && !isListed(INVALID_CARDINALITY_SUBJECTS, ch);
            ch = Character.toLowerCase(ch);
            if (optional) {
                // cardinality
                char next = i + 1 < template.length() ? template.charAt(i + 1) : '<';
                doRead = Character.isUpperCase(next)
                        && !isListed(INVALID_CARDINALITY_SUBJECTS, next);
                next = Character.toLowerCase(next);
                if (isListed(INVALID_CARDINALITY_SUBJECTS, next)) {
                    SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_SVN_MALFORMED_DATA, "Malformed template data, ''{0}''", templateStr));
                }
                i++;
                ch = next;
            }
            is.mark(0x200);
            Object result = null;
            try {
                if (ch == 'b' || ch == 'i' || ch == 's') {
                    if (ch == 'b') {
                        result = readStringAsBytes(is);
                    } else if (ch == 's') {
                        result = readString(is);
                    } else {
                        result = createDelegatingStream(is);
                    }
                } else if (ch == 'p') {
                    readChar(is, '(');
                    String name = readString(is);
                    String value = null;
                    // may not be there
                    is.mark(0x100);
                    try {
                        value = readString(is);
                        is.mark(-1); // drop the last mark
                    } catch (SVNException exception) {
                        try {
                            value = null;
                            is.reset();
                        } catch (IOException e1) {
                        }
                    }
                    readChar(is, ')');
                    result = new String[] { name, value };
                } else if (ch == 'z') {
                    readChar(is, '(');
                    String name = readString(is);
                    String value = null;
                    // may not be there
                    readChar(is, '(');
                    is.mark(0x100);
                    try {
                        value = readString(is);
                        is.mark(-1); // drop the last mark
                    } catch (SVNException exception) {
                        try {
                            value = null;
                            is.reset();
                        } catch (IOException e1) {
                        }
                    }
                    readChar(is, ')');
                    readChar(is, ')');
                    result = new String[] { name, value };
                } else if (ch == 'w') {
                    String word = readWord(is);
                    result = word;
                } else if (ch == 't') {
                    result = Boolean.valueOf(readBoolean(is));
                } else if (ch == 'n') {
                    result = new Long(readNumber(is));
                } else if (ch == '[') {
                    readChar(is, '(');
                    String word = readWord(is);
                    if ("failure".equals(word)) {
                        // read errors and throw
                        readChar(is, '(');
                        List errorMessages = new ArrayList();
                        try {
                            while (true) {
                                is.mark(0x100);
                                SVNErrorMessage err = readError(is);
                                errorMessages.add(err);
                                is.mark(-1); // drop the last mark
                            }
                        } catch (SVNException e) {
                            is.reset();
                            readChar(is, ')');
                            readChar(is, ')');
                        } 
                        unconditionalThrow = true;
                        SVNErrorMessage topError = (SVNErrorMessage) errorMessages.get(0);
                        for(int k = 1; k < errorMessages.size(); k++) {
                            SVNErrorMessage child = (SVNErrorMessage) errorMessages.get(k);
                            topError.setChildErrorMessage(child);
                            topError = child;
                        }   
                        SVNErrorManager.error((SVNErrorMessage) errorMessages.get(0));
                    } else if (!"success".equals(word)) {
                        SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_SVN_MALFORMED_DATA, "Unknown status ''{0}'' in command response", word));
                    }
                } else if (ch == ')' || ch == ']') {
                    readChar(is, ')');
                } else if (ch == '(') {
                    readChar(is, '(');
                } else if (ch == 'd') {
                    result = readDirEntry(is);
                } else if (ch == 'f') {
                    result = readStatEntry(is);
                } else if (ch == 'e') {
                    if (editorBaton == null) {
                        editorBaton = new SVNEditModeReader();
                        if (target[targetIndex] instanceof ISVNEditor) {
                            editorBaton
                                    .setEditor((ISVNEditor) target[targetIndex]);
                        }
                    }
                    readChar(is, '(');
                    String commandName = readWord(is);
                    boolean hasMore = false;
                    try {
                        hasMore = editorBaton.processCommand(commandName, is);
                    } catch (Throwable th) {
                        unconditionalThrow = true;
                        SVNDebugLog.getDefaultLog().info(th);
                        if (th instanceof SVNException) {
                            throw ((SVNException) th);
                        }
                        SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_SVN_IO_ERROR, th.getMessage()), th);
                    }
                    if (!"textdelta-chunk".equals(commandName)) {
                        readChar(is, ')');
                    }
                    if (!hasMore) {
                        return target;
                    }
                } else if (ch == 'x') {
                    try {
                        String word = readWord(is);
                        if (!"done".equals(word)) {
                            SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_SVN_MALFORMED_DATA, "Malformed network data, 'done' expected"));
                        }
                    } catch (SVNException e) {
                        if (e.getErrorMessage().getErrorCode() == SVNErrorCode.RA_SVN_MALFORMED_DATA) {
                            SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_SVN_MALFORMED_DATA, "Malformed network data, 'done' expected"));
                        }
                    }
                } else if (ch == 'l') {
                    result = readLock(is);//new RollbackInputStream(is));
                }
                if (doRead) {
                    target = reportResult(target, targetIndex, result, multiple);
                }
                if (multiple) {
                    i -= 2;
                } else if (doRead) {
                    targetIndex++;
                }
            } catch (SVNException e) {
                if (unconditionalThrow || e instanceof SVNCancelException || e instanceof SVNAuthenticationException) {
                    throw e;
                }
                try {
                    is.reset();
                } catch (IOException e1) {
                    //
                }
                if (optional) {
                    if (doRead) {
                        targetIndex++;
                    }
                } else {
                    throw e;
                }
            } catch (IOException ioException) {
                SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_SVN_IO_ERROR, ioException.getMessage()));
            }
            is.mark(-1); // drop the last mark
        }
        if (target == null) {
            target = new Object[0];
        }
        return target;
    }

    private static final char[] VALID_TEMPLATE_CHARS = { '(', ')', '[', ']', // groups
            's', 'w', 'b', 'i', 'n', 't', 'p', // items
            'd', 'f', 'l', 'a', 'r', 'e', 'x', 'l', // command-specific
            '?', '*', 'z' };

    private static final char[] INVALID_CARDINALITY_SUBJECTS = { '(', ')', '[',
            ']', '?', '*', '<' };

    private static Object[] reportResult(Object[] target, int index, Object result, boolean multiple) throws SVNException {
        // capacity
        if (target == null) {
            target = new Object[index + 1];
        } else if (index >= target.length) {
            Object[] array = new Object[index + 1];
            System.arraycopy(target, 0, array, 0, target.length);
            target = array;
        }
        // delegating
        if (target[index] instanceof ISVNDirEntryHandler) {
            ISVNDirEntryHandler handler = ((ISVNDirEntryHandler) target[index]);
            if (result instanceof SVNDirEntry) {
                handler.handleDirEntry((SVNDirEntry) result);
            }
        } else if (target[index] == null) {
            if (result instanceof String[]) {
                target[index] = new HashMap();
            } else if (multiple) {
                target[index] = new LinkedList();
            } else {
                target[index] = result;
            }
        } else if (target[index] instanceof OutputStream && result instanceof InputStream) {
            InputStream in = (InputStream) result;
            OutputStream out = (OutputStream) target[index];
            byte[] buffer = new byte[2048];
            boolean cancelled = false;
            try {
                while (true) {
                    int read = in.read(buffer);
                    if (read > 0) {
                        out.write(buffer, 0, read);
                    } else {
                        break;
                    }
                }
                out.flush();
            } catch (IOException e) {
                cancelled = true;
                if (e instanceof SVNCancellableOutputStream.IOCancelException) {
                    SVNErrorManager.cancel(e.getMessage());
                }
                //
            } finally {
                // no need to do that if operation was cancelled!
                try {
                    while (!cancelled && in.read(buffer) > 0) {
                    }
                } catch (IOException e1) {
                    //
                }
            }
        }
        if (target[index] instanceof List) {
            ((List) target[index]).add(result);
        } else if (target[index] instanceof Map && result instanceof String[]) {
            String[] property = (String[]) result;
            ((Map) target[index]).put(property[0], property[1]);
        }
        return target;
    }

    private static StringBuffer normalizeTemplate(String template) throws SVNException {
        StringBuffer sb = new StringBuffer(template.length());
        for (int i = 0; i < template.length(); i++) {
            char ch = template.charAt(i);
            if (Character.isWhitespace(ch)) {
                continue;
            }
            char tch = Character.toLowerCase(ch);
            if (isListed(VALID_TEMPLATE_CHARS, tch)) {
                sb.append(ch);
                continue;
            }
            SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_SVN_MALFORMED_DATA, "Template ''{0}'' is not valid", template));
        }
        return sb;
    }

    private static boolean isListed(char[] chars, char test) {
        for (int i = 0; i < chars.length; i++) {
            if (test == chars[i]) {
                return true;
            }
        }
        return false;
    }

    private static char skipWhitespace(InputStream is) throws IOException {
        while (true) {
            char read = (char) is.read();
            if (Character.isWhitespace(read)) {
                continue;
            }
            return read;
        }
    }

    private static byte[] readStringAsBytes(InputStream is) throws IOException, SVNException {
        int length = readStringLength(is);
        return readBytes(is, length, null);
    }

    private static String readString(InputStream is) throws IOException, SVNException {
        int length = readStringLength(is);
        return new String(readBytes(is, length, null), 0, length, "UTF-8");
    }

    private static int readStringLength(InputStream is) throws IOException, SVNException {
        char ch = skipWhitespace(is);
        int length = 0;
        while (Character.isDigit(ch)) {
            length *= 10;
            length += (ch - '0');
            ch = (char) is.read();
        }
        if (ch == ':') {
            return length;
        }
        SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_SVN_MALFORMED_DATA, "Malformed network data, string length expected"), false);
        return -1;
    }

    private static int readNumber(InputStream is) throws IOException, SVNException {
        char ch = skipWhitespace(is);
        int length = 0;
        while (Character.isDigit(ch)) {
            length *= 10;
            length += (ch - '0');
            ch = (char) is.read();
        }
        if (Character.isWhitespace(ch)) {
            return length;
        }
        SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_SVN_MALFORMED_DATA, "Malformed network data, number expected"), false);
        return -1;
    }

    private static String readWord(InputStream is) throws IOException, SVNException {
        char ch = skipWhitespace(is);
        StringBuffer buffer = new StringBuffer();
        int count = 0;
        while (true) {
            if (Character.isWhitespace(ch)) {
                break;
            }
            if (count == 0 && !Character.isLetter(ch)) {
                SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_SVN_MALFORMED_DATA, "Malformed network data"), false);
            } else if (count > 0
                    && !(Character.isLetterOrDigit(ch) || ch == '-')) {
                SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_SVN_MALFORMED_DATA, "Malformed network data"), false);
            }
            buffer.append(ch);
            count++;
            ch = (char) is.read();
        }
        return buffer.toString();
    }

    private static boolean readBoolean(InputStream is) throws IOException, SVNException {
        String word = readWord(is);
        if ("true".equalsIgnoreCase(word)) {
            return true;
        } else if ("false".equalsIgnoreCase(word)) {
            return false;
        }
        SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_SVN_MALFORMED_DATA, "Malformed network data, 'true' or 'false' expected"), false);
        return false;
    }

    private static void readChar(InputStream is, char test) throws IOException, SVNException {
        char ch = skipWhitespace(is);
        if (ch != test) {
            if (ch < 0 || ch == Character.MAX_VALUE) {
                SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_SVN_CONNECTION_CLOSED));
            }
            SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_SVN_MALFORMED_DATA, "Malformed network data"), false);
        }
    }

    private static byte[] readBytes(InputStream is, int length, byte[] buffer) throws IOException {
        if (buffer == null || buffer.length < length) {
            buffer = new byte[length];
        }
        int offset = 0;
        while (offset < length) {
            int r = is.read(buffer, offset, length - offset);
            if (r <= 0) {
                throw new IOException("Input/Output error while receiving svn data");
            }
            offset += r;
        }
        return buffer;
    }

    private static InputStream createDelegatingStream(final InputStream source) throws IOException, SVNException {
        int length = readStringLength(source);
        final int[] counter = new int[] { length };
        return new InputStream() {
            public int read() throws IOException {
                while (counter[0] > 0) {
                    counter[0]--;
                    return source.read();
                }
                return -1;
            }
        };
    }

    private static SVNErrorMessage readError(InputStream is) throws IOException, SVNException {
        InputStream pis = is;
        readChar(pis, '(');
        int code = readNumber(pis);
        String errorMessage = readString(pis);
        readString(pis);
        readNumber(pis);
        readChar(pis, ')');
        SVNErrorCode errorCode = SVNErrorCode.getErrorCode(code);        
        return SVNErrorMessage.create(errorCode, errorMessage);
    }

    private static SVNDirEntry readDirEntry(InputStream is) throws SVNException {
        Object[] items = SVNReader.parse(is, "(SWNTN(?S)(?S))", null);

        String name = SVNReader.getString(items, 0);
        SVNNodeKind kind = SVNNodeKind.parseKind(SVNReader.getString(items, 1));
        long size = SVNReader.getLong(items, 2);
        boolean hasProps = SVNReader.getBoolean(items, 3);
        long revision = SVNReader.getLong(items, 4);
        Date date = items[5] != null ? SVNTimeUtil.parseDate(SVNReader.getString(items, 5)) : null;
        String author = SVNReader.getString(items, 6);
        return new SVNDirEntry(null, name, kind, size, hasProps, revision, date, author);
    }

    private static SVNDirEntry readStatEntry(InputStream is) throws SVNException {
        Object[] items = SVNReader.parse(is, "(WNTN(?S)(?S))", null);

        SVNNodeKind kind = SVNNodeKind.parseKind(SVNReader.getString(items, 0));
        long size = SVNReader.getLong(items, 1);
        boolean hasProps = SVNReader.getBoolean(items, 2);
        long revision = SVNReader.getLong(items, 3);
        Date date = items[4] != null ? SVNTimeUtil.parseDate(SVNReader.getString(items, 4)) : null;
        String author = SVNReader.getString(items, 5);
        return new SVNDirEntry(null, null, kind, size, hasProps, revision, date, author);
    }

    private static SVNLock readLock(InputStream is) throws SVNException {
        Object[] items = SVNReader.parse(is, "(SSS(?S)S(?S))", new Object[6]);

        String path = (String) items[0];
        String id = (String) items[1];
        String owner = (String) items[2];
        String comment = (String) items[3];
        String creationDate = (String) items[4];
        String expirationDate = (String) items[5];
        Date created = creationDate != null ? SVNTimeUtil.parseDate(creationDate)
                : null;
        Date expires = expirationDate != null ? SVNTimeUtil
                .parseDate(expirationDate) : null;

        return new SVNLock(path, id, owner, comment, created, expires);
    }

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.