dap4/d4servlet/src/main/java/dap4/servlet/DapRequest.java
protected void
    parse()
            throws IOException
    {
        this.url = request.getRequestURL().toString(); // does not include query
        // The mock servlet code does not construct a query string,
        // so if we are doing testing, construct from parametermap.
        if(DapController.TESTING) {
            this.querystring = makeQueryString(this.request);
        } else {
            this.querystring = request.getQueryString();    // raw (undecoded)
        }

        XURI xuri;
        try {
            xuri = new XURI(this.url).parseQuery(this.querystring);
        } catch (URISyntaxException e) {
            throw new IOException(e);
        }
        // Now, construct various items
        StringBuilder buf = new StringBuilder();
        buf.append(request.getScheme());
        buf.append("://");
        buf.append(request.getServerName());
        int port = request.getServerPort();
        if(port > 0) {
            buf.append(":");
            buf.append(port);
        }
        this.server = buf.toString();

        // There appears to be some inconsistency in how the url path is divided up
        // depending on if this is a spring controller vs a raw servlet.
        // Try to canonicalize so that context path always ends with id
        // and servletpath does not start with it.
        String id = controller.getServletID();
        String sp = DapUtil.canonicalpath(request.getServletPath());
        String cp = DapUtil.canonicalpath(request.getContextPath());
        if(!cp.endsWith(id)) // probably spring ; contextpath does not ends with id
            cp = cp + "/" + id;
        buf.append(cp);
        this.controllerpath = buf.toString();
        sp = HTTPUtil.relpath(sp);
        if(sp.startsWith(id)) // probably spring also
            sp = sp.substring(id.length());

        this.datasetpath = HTTPUtil.relpath(sp);
        this.datasetpath = DapUtil.nullify(this.datasetpath);

        this.mode = null;
        if(this.datasetpath == null) {
            // Presume mode is a capabilities request
            this.mode = RequestMode.CAPABILITIES;
            this.format = ResponseFormat.HTML;
        } else {
            // Decompose path by '.'
            String[] pieces = this.datasetpath.split("[.]");
            // Search backward looking for the mode (dmr or dap)
            // meanwhile capturing the format extension
            int modepos = 0;
            for(int i = pieces.length - 1; i >= 1; i--) {//ignore first piece
                String ext = pieces[i];
                // We assume that the set of response formats does not interset the set of request modes
                RequestMode mode = RequestMode.modeFor(ext);
                ResponseFormat format = ResponseFormat.formatFor(ext);
                if(mode != null) {
                    // Stop here
                    this.mode = mode;
                    modepos = i;
                    break;
                } else if(format != null) {
                    if(this.format != null)
                        throw new DapException("Multiple response formats specified: " + ext)
                                .setCode(HttpServletResponse.SC_BAD_REQUEST);
                    this.format = format;
                }
            }
            // Set the datasetpath to the entire path before the mode defining extension.
            if(modepos > 0)
                this.datasetpath = DapUtil.join(pieces, ".", 0, modepos);
        }

        if(this.mode == null)
            this.mode = RequestMode.DSR;
        if(this.format == null)
            this.format = ResponseFormat.NONE;

        // Parse the query string into a Map
        if(querystring != null && querystring.length() > 0)
            this.queries = xuri.getQueryFields();

        // For testing purposes, get the desired endianness to use with replies
        String p = queryLookup(Dap4Util.DAP4ENDIANTAG);
        if(p != null) {
            Integer oz = DapUtil.stringToInteger(p);
            if(oz == null)
                this.order = ByteOrder.LITTLE_ENDIAN;
            else
                this.order = (oz != 0 ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
        }

        // Ditto for no checksum
        p = queryLookup(Dap4Util.DAP4CSUMTAG);
        if(p != null) {
            this.checksummode = ChecksumMode.modeFor(p);
        }
        if(this.checksummode == null)
            this.checksummode = DEFAULTCSUM;

        if(DEBUG) {
            DapLog.debug("DapRequest: controllerpath =" + this.controllerpath);
            DapLog.debug("DapRequest: extension=" + (this.mode == null ? "null" : this.mode.extension()));
            DapLog.debug("DapRequest: datasetpath=" + this.datasetpath);
        }
    }
protected void
    parse()
            throws IOException
    {
        this.url = request.getRequestURL().toString(); // does not include query
        // The mock servlet code does not construct a query string,
        // so if we are doing testing, construct from parametermap.
        if(DapController.TESTING) {
            this.querystring = makeQueryString(this.request);
        } else {
            this.querystring = request.getQueryString();    // raw (undecoded)
        }

        XURI xuri;
        try {
            xuri = new XURI(this.url).parseQuery(this.querystring);
        } catch (URISyntaxException e) {
            throw new IOException(e);
        }
        // Now, construct various items
        StringBuilder buf = new StringBuilder();
        buf.append(request.getScheme());
        buf.append("://");
        buf.append(request.getServerName());
        int port = request.getServerPort();
        if(port > 0) {
            buf.append(":");
            buf.append(port);
        }
        this.server = buf.toString();

        // There appears to be some inconsistency in how the url path is divided up
        // depending on if this is a spring controller vs a raw servlet.
        // Try to canonicalize so that context path always ends with id
        // and servletpath does not start with it.
        String id = controller.getServletID();
        String sp = DapUtil.canonicalpath(request.getServletPath());
        String cp = DapUtil.canonicalpath(request.getContextPath());
        if(!cp.endsWith(id)) // probably spring ; contextpath does not ends with id
            cp = cp + "/" + id;
        buf.append(cp);
        this.controllerpath = buf.toString();
        sp = HTTPUtil.relpath(sp);
        if(sp.startsWith(id)) // probably spring also
            sp = sp.substring(id.length());

        this.datasetpath = HTTPUtil.relpath(sp);
        this.datasetpath = DapUtil.nullify(this.datasetpath);

        this.mode = null;
        if(this.datasetpath == null) {
            // Presume mode is a capabilities request
            this.mode = RequestMode.CAPABILITIES;
            this.format = ResponseFormat.HTML;
        } else {
            // Decompose path by '.'
            String[] pieces = this.datasetpath.split("[.]");
            // Search backward looking for the mode (dmr or dap)
            // meanwhile capturing the format extension
            int modepos = 0;
            for(int i = pieces.length - 1; i >= 1; i--) {//ignore first piece
                String ext = pieces[i];
                // We assume that the set of response formats does not interset the set of request modes
                RequestMode mode = RequestMode.modeFor(ext);
                ResponseFormat format = ResponseFormat.formatFor(ext);
                if(mode != null) {
                    // Stop here
                    this.mode = mode;
                    modepos = i;
                    break;
                } else if(format != null) {
                    if(this.format != null)
                        throw new DapException("Multiple response formats specified: " + ext)
                                .setCode(HttpServletResponse.SC_BAD_REQUEST);
                    this.format = format;
                }
            }
            // Set the datasetpath to the entire path before the mode defining extension.
            if(modepos > 0)
                this.datasetpath = DapUtil.join(pieces, ".", 0, modepos);
        }

        if(this.mode == null)
            this.mode = RequestMode.DSR;
        if(this.format == null)
            this.format = ResponseFormat.NONE;

        // Parse the query string into a Map
        if(querystring != null && querystring.length() > 0)
            this.queries = xuri.getQueryFields();

        // For testing purposes, get the desired endianness to use with replies
        String p = queryLookup(Dap4Util.DAP4ENDIANTAG);
        if(p != null) {
            Integer oz = DapUtil.stringToInteger(p);
            if(oz == null)
                this.order = ByteOrder.LITTLE_ENDIAN;
            else
                this.order = (oz != 0 ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
        }

        // Ditto for no checksum
        p = queryLookup(Dap4Util.DAP4CSUMTAG);
        if(p != null) {
            this.checksummode = ChecksumMode.modeFor(p);
        }
        if(this.checksummode == null)
            this.checksummode = DEFAULTCSUM;

        if(DEBUG) {
            DapLog.debug("DapRequest: controllerpath =" + this.controllerpath);
            DapLog.debug("DapRequest: extension=" + (this.mode == null ? "null" : this.mode.extension()));
            DapLog.debug("DapRequest: datasetpath=" + this.datasetpath);
        }
    }
The goal of parse() is to extract info
from the underlying HttpRequest and cache it
in this object.
<p>
In particular, the incoming URL needs to be decomposed
into multiple pieces. Certain assumptions are made:
1. every incoming url is of the form
(a) http(s)://host:port/d4ts/
or
(b) http(s)://host:port/d4ts/<datasetpath>?query
Case a indicates that the front page is to be returned.
Case b indicates a request for a dataset (or dsr), and its
value is determined by its extensions. The query may be absent.
We want to extract the following pieces.
1. (In URI parlance) The scheme plus the authority:
http://host:port
3. The return type: depending on the last extension (e.g. ".txt").
4. The requested value: depending on the next to last extension (e.g. ".dap").
5. The suffix path specifying the actual dataset: datasetpath
with return and request type extensions removed.
6. The url path = servletpath + datasetpath.
7. The query part.