<commit_before>/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 *
 *    (C) 2002-2016, Open Source Geospatial Foundation (OSGeo)
 *
 *    This library 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;
 *    version 2.1 of the License.
 *
 *    This library 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.
 */
package org.geotools.data.wfs.internal.parsers;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PushbackInputStream;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.opengis.wfs.GetFeatureType;

import org.geotools.data.ows.HTTPResponse;
import org.geotools.data.wfs.WFSDataStore;
import org.geotools.data.wfs.internal.GetFeatureParser;
import org.geotools.data.wfs.internal.GetFeatureRequest;
import org.geotools.data.wfs.internal.GetFeatureResponse;
import org.geotools.data.wfs.internal.Loggers;
import org.geotools.data.wfs.internal.WFSException;
import org.geotools.data.wfs.internal.WFSOperationType;
import org.geotools.data.wfs.internal.WFSRequest;
import org.geotools.data.wfs.internal.WFSResponse;
import org.geotools.data.wfs.internal.WFSResponseFactory;
import org.geotools.ows.ServiceException;
import org.geotools.xml.Parser;

/**
 * An abstract WFS response parser factory for GetFeature requests in GML output formats.
 */
@SuppressWarnings("nls")
public abstract class AbstractGetFeatureResponseParserFactory implements WFSResponseFactory {

    private static final Logger LOGGER = Loggers.MODULE;

    /**
     * @see WFSResponseFactory#isAvailable()
     */
    public boolean isAvailable() {
        return true;
    }

    /**
     * Checks if this factory can create a parser for the potential responses of the given WFS
     * request.
     * <p>
     * For instance, this factory can create a parser as long as the request is a
     * {@link GetFeatureType GetFeature} request and the request output format matches
     * {@code "text/xml; subtype=gml/3.1.1"}.
     * </p>
     * 
     * @see WFSResponseFactory#canProcess(WFSOperationType, String)
     */
    public boolean canProcess(final WFSRequest request, final String contentType) {
        if (!WFSOperationType.GET_FEATURE.equals(request.getOperation())) {
            return false;
        }
        if (!getSupportedVersions().contains(request.getStrategy().getVersion())) {
            return false;
        }
        // String outputFormat = ((GetFeatureRequest) request).getOutputFormat();
        boolean matches = getSupportedOutputFormats().contains(contentType);
        if (!matches) {
            // fuzy search, "
            for (String supported : getSupportedOutputFormats()) {
                if (supported.startsWith(contentType) || contentType.startsWith(supported)) {
                    matches = true;
                    break;
                }
            }
        }
        return matches;
    }

    /**
     * Returns either a {@link FeatureCollectionParser} or an {@link ExceptionReportParser}
     * depending on what the server returned.
     * <p>
     * Ideally, the decision should only be taken based on the WFS response's content-type HTTP
     * header. Truth is, some WFS implementations does not set proper HTTP response headers so a bit
     * of an heuristic may be needed in order to identify the actual response.
     * </p>
     * 
     * @see WFSResponseFactory#createParser(WFSResponse)
     * @see FeatureCollectionParser
     * @see ExceptionReportParser
     */
    public WFSResponse createResponse(WFSRequest request, HTTPResponse response) throws IOException {

        // We can't rely on the server returning the correct output format. Some, for example
        // CubeWerx, upon a successful GetFeature request, set the response's content-type
        // header to plain "text/xml" instead of "text/xml;subtype=gml/3.1.1". So we'll do a bit
        // of heuristics to find out what it actually returned
        final int buffSize;
        if (LOGGER.isLoggable(Level.FINER)) {
            buffSize = 4096;
        } else {
            buffSize = 512;
        }
        PushbackInputStream pushbackIn = new PushbackInputStream(response.getResponseStream(), buffSize);
        byte[] buff = new byte[buffSize];
        int readCount = 0;
        int r;
        while ((r = pushbackIn.read(buff, readCount, buffSize - readCount)) != -1) {
            readCount += r;
            if (readCount == buffSize) {
                break;
            }
        }

        String charset = response.getResponseHeader("Charset");
        try {
            Charset.forName(charset);
        } catch (Exception e) {
            charset = "UTF-8";
        }

        StringBuilder head = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(
                new ByteArrayInputStream(buff), charset))) {
            String line;
            while ((line = reader.readLine()) != null) {
                head.append(line).append('\n');
            }
        }
        if (LOGGER.isLoggable(Level.FINER)) {
            System.err.println("Response head:");
            System.err.println(head);
        }

        pushbackIn.unread(buff, 0, readCount);

        if (head.indexOf("FeatureCollection") > 0) {
            GetFeatureParser parser = parser((GetFeatureRequest) request, pushbackIn);
            try {
                return new GetFeatureResponse(request, response, parser);
            } catch (ServiceException e) {
                throw new IOException(e);
            }
        } else if (head.indexOf("ExceptionReport") > 0) {
            throw parseException(request, pushbackIn);
        } else {
            throw new IllegalStateException("Unkown server response: " + head);
        }
    }
    
    /**
     * @param wfs
     *            the {@link WFSDataStore} that sent the request
     * @param response
     *            a response handle to a service exception report
     * @return a {@link WFSException} containing the server returned exception report messages
     * @see WFSResponseParser#parse(WFSProtocol, WFSResponse)
     */<commit_msg>Stop System.err.println flood that breaks Travis CI<commit_after>/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 *
 *    (C) 2002-2016, Open Source Geospatial Foundation (OSGeo)
 *
 *    This library 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;
 *    version 2.1 of the License.
 *
 *    This library 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.
 */
package org.geotools.data.wfs.internal.parsers;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PushbackInputStream;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.opengis.wfs.GetFeatureType;

import org.geotools.data.ows.HTTPResponse;
import org.geotools.data.wfs.WFSDataStore;
import org.geotools.data.wfs.internal.GetFeatureParser;
import org.geotools.data.wfs.internal.GetFeatureRequest;
import org.geotools.data.wfs.internal.GetFeatureResponse;
import org.geotools.data.wfs.internal.Loggers;
import org.geotools.data.wfs.internal.WFSException;
import org.geotools.data.wfs.internal.WFSOperationType;
import org.geotools.data.wfs.internal.WFSRequest;
import org.geotools.data.wfs.internal.WFSResponse;
import org.geotools.data.wfs.internal.WFSResponseFactory;
import org.geotools.ows.ServiceException;
import org.geotools.xml.Parser;

/**
 * An abstract WFS response parser factory for GetFeature requests in GML output formats.
 */
@SuppressWarnings("nls")
public abstract class AbstractGetFeatureResponseParserFactory implements WFSResponseFactory {

    private static final Logger LOGGER = Loggers.MODULE;

    /**
     * @see WFSResponseFactory#isAvailable()
     */
    public boolean isAvailable() {
        return true;
    }

    /**
     * Checks if this factory can create a parser for the potential responses of the given WFS
     * request.
     * <p>
     * For instance, this factory can create a parser as long as the request is a
     * {@link GetFeatureType GetFeature} request and the request output format matches
     * {@code "text/xml; subtype=gml/3.1.1"}.
     * </p>
     * 
     * @see WFSResponseFactory#canProcess(WFSOperationType, String)
     */
    public boolean canProcess(final WFSRequest request, final String contentType) {
        if (!WFSOperationType.GET_FEATURE.equals(request.getOperation())) {
            return false;
        }
        if (!getSupportedVersions().contains(request.getStrategy().getVersion())) {
            return false;
        }
        // String outputFormat = ((GetFeatureRequest) request).getOutputFormat();
        boolean matches = getSupportedOutputFormats().contains(contentType);
        if (!matches) {
            // fuzy search, "
            for (String supported : getSupportedOutputFormats()) {
                if (supported.startsWith(contentType) || contentType.startsWith(supported)) {
                    matches = true;
                    break;
                }
            }
        }
        return matches;
    }

    /**
     * Returns either a {@link FeatureCollectionParser} or an {@link ExceptionReportParser}
     * depending on what the server returned.
     * <p>
     * Ideally, the decision should only be taken based on the WFS response's content-type HTTP
     * header. Truth is, some WFS implementations does not set proper HTTP response headers so a bit
     * of an heuristic may be needed in order to identify the actual response.
     * </p>
     * 
     * @see WFSResponseFactory#createParser(WFSResponse)
     * @see FeatureCollectionParser
     * @see ExceptionReportParser
     */
    public WFSResponse createResponse(WFSRequest request, HTTPResponse response) throws IOException {

        // We can't rely on the server returning the correct output format. Some, for example
        // CubeWerx, upon a successful GetFeature request, set the response's content-type
        // header to plain "text/xml" instead of "text/xml;subtype=gml/3.1.1". So we'll do a bit
        // of heuristics to find out what it actually returned
        final int buffSize;
        if (LOGGER.isLoggable(Level.FINER)) {
            buffSize = 4096;
        } else {
            buffSize = 512;
        }
        PushbackInputStream pushbackIn = new PushbackInputStream(response.getResponseStream(), buffSize);
        byte[] buff = new byte[buffSize];
        int readCount = 0;
        int r;
        while ((r = pushbackIn.read(buff, readCount, buffSize - readCount)) != -1) {
            readCount += r;
            if (readCount == buffSize) {
                break;
            }
        }

        String charset = response.getResponseHeader("Charset");
        try {
            Charset.forName(charset);
        } catch (Exception e) {
            charset = "UTF-8";
        }

        StringBuilder head = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(
                new ByteArrayInputStream(buff), charset))) {
            String line;
            while ((line = reader.readLine()) != null) {
                head.append(line).append('\n');
            }
        }
        if (LOGGER.isLoggable(Level.FINER)) {
            LOGGER.finer("Response head:\n" + head);
        }

        pushbackIn.unread(buff, 0, readCount);

        if (head.indexOf("FeatureCollection") > 0) {
            GetFeatureParser parser = parser((GetFeatureRequest) request, pushbackIn);
            try {
                return new GetFeatureResponse(request, response, parser);
            } catch (ServiceException e) {
                throw new IOException(e);
            }
        } else if (head.indexOf("ExceptionReport") > 0) {
            throw parseException(request, pushbackIn);
        } else {
            throw new IllegalStateException("Unkown server response: " + head);
        }
    }
    
    /**
     * @param wfs
     *            the {@link WFSDataStore} that sent the request
     * @param response
     *            a response handle to a service exception report
     * @return a {@link WFSException} containing the server returned exception report messages
     * @see WFSResponseParser#parse(WFSProtocol, WFSResponse)
     */
{'fraction_non_alphanumeric': 0.09238188531384221, 'fraction_numerical': 0.004370431768517821, 'mean_word_length': 2.571582346609257, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 8}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3322775', 'n_tokens_mistral': 3649, 'n_tokens_neox': 3561, 'n_words': 1054}