org.exist.xquery.functions.fn.FunUnparsedText.java Source code

Java tutorial

Introduction

Here is the source code for org.exist.xquery.functions.fn.FunUnparsedText.java

Source

/*
 * eXist Open Source Native XML Database
 * Copyright (C) 2001-2017 The eXist Project
 * http://exist-db.org
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package org.exist.xquery.functions.fn;

import org.apache.commons.io.IOUtils;
import org.exist.dom.QName;
import org.exist.security.PermissionDeniedException;
import org.exist.source.Source;
import org.exist.source.SourceFactory;
import org.exist.util.PatternFactory;
import org.exist.xmldb.XmldbURI;
import org.exist.xquery.*;
import org.exist.xquery.value.*;

import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.regex.Pattern;

import static org.exist.xquery.FunctionDSL.*;

public class FunUnparsedText extends BasicFunction {

    private final static FunctionParameterSequenceType PARAM_HREF = optParam("href", Type.STRING,
            "the URI to load text from");
    private final static FunctionParameterSequenceType PARAM_ENCODING = param("encoding", Type.STRING,
            "character encoding of the resource");

    static final FunctionSignature[] FS_UNPARSED_TEXT = functionSignatures(
            new QName("unparsed-text", Function.BUILTIN_FUNCTION_NS),
            "reads an external resource (for example, a file) and returns a string representation of the resource",
            returnsOpt(Type.STRING), arities(arity(PARAM_HREF), arity(PARAM_HREF, PARAM_ENCODING)));

    static final FunctionSignature[] FS_UNPARSED_TEXT_LINES = functionSignatures(
            new QName("unparsed-text-lines", Function.BUILTIN_FUNCTION_NS),
            "reads an external resource (for example, a file) and returns its contents as a sequence of strings, one for each line of text in the string representation of the resource",
            returnsOptMany(Type.STRING), arities(arity(PARAM_HREF), arity(PARAM_HREF, PARAM_ENCODING)));

    static final FunctionSignature[] FS_UNPARSED_TEXT_AVAILABLE = functionSignatures(
            new QName("unparsed-text-available", Function.BUILTIN_FUNCTION_NS),
            "determines whether a call on the fn:unparsed-text function with identical arguments would return a string",
            returnsOpt(Type.STRING), arities(arity(PARAM_HREF), arity(PARAM_HREF, PARAM_ENCODING)));

    public FunUnparsedText(final XQueryContext context, final FunctionSignature signature) {
        super(context, signature);
    }

    @Override
    public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathException {
        final String encoding = args.length == 2 ? args[1].getStringValue() : null;
        if (!args[0].isEmpty()) {
            if (isCalledAs("unparsed-text-lines")) {
                return readLines(args[0].getStringValue(), encoding);
            } else {
                final String content;
                try {
                    content = readContent(args[0].getStringValue(), encoding);
                } catch (XPathException e) {
                    if (isCalledAs("unparsed-text-available")) {
                        return BooleanValue.FALSE;
                    }
                    throw e;
                }
                if (isCalledAs("unparsed-text-available")) {
                    return BooleanValue.TRUE;
                }
                return new StringValue(content);
            }
        }
        return Sequence.EMPTY_SEQUENCE;
    }

    private String readContent(final String uriParam, final String encoding) throws XPathException {
        try {
            final Source source = getSource(uriParam);
            Charset charset = getCharset(encoding, source);
            final StringWriter output = new StringWriter();
            try (final InputStream is = source.getInputStream()) {
                IOUtils.copy(is, output, charset);
            }
            return output.toString();
        } catch (IOException e) {
            throw new XPathException(this, ErrorCodes.FOUT1170, e.getMessage());
        }
    }

    private Sequence readLines(final String uriParam, final String encoding) throws XPathException {
        try {
            final Sequence result = new ValueSequence();
            final Source source = getSource(uriParam);
            final Charset charset = getCharset(encoding, source);

            try (final BufferedReader reader = new BufferedReader(
                    new InputStreamReader(source.getInputStream(), charset))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    result.add(new StringValue(line));
                }
            }
            return result;
        } catch (IOException e) {
            throw new XPathException(this, ErrorCodes.FOUT1170, e.getMessage());
        }
    }

    private Charset getCharset(final String encoding, final Source source) throws XPathException {
        Charset charset;
        if (encoding == null) {
            try {
                charset = source.getEncoding();
            } catch (IOException e) {
                throw new XPathException(this, ErrorCodes.FOUT1170, e.getMessage());
            }
            if (charset == null) {
                charset = StandardCharsets.UTF_8;
            }
        } else {
            try {
                charset = Charset.forName(encoding);
            } catch (IllegalArgumentException e) {
                throw new XPathException(this, ErrorCodes.FOUT1190, e.getMessage());
            }
        }
        return charset;
    }

    private Source getSource(final String uriParam) throws XPathException {
        try {
            URI uri = new URI(uriParam);
            if (uri.getScheme() == null) {
                uri = new URI(XmldbURI.EMBEDDED_SERVER_URI_PREFIX + uriParam);
            }
            if (uri.getFragment() != null) {
                throw new XPathException(this, ErrorCodes.FOUT1170,
                        "href argument may not contain fragment identifier");
            }
            final Source source = SourceFactory.getSource(context.getBroker(), "", uri.toASCIIString(), false);
            if (!context.getBroker().getCurrentSubject().hasDbaRole()) {
                throw new XPathException(this, ErrorCodes.FOUT1170,
                        "non-dba user not allowed to read from file system");
            }
            return source;
        } catch (IOException | PermissionDeniedException | URISyntaxException e) {
            throw new XPathException(this, ErrorCodes.FOUT1170, e.getMessage());
        }
    }
}