Uri.java
/*
** Module : Uri.java
** Abstract : Implementation of the builtin class.
**
** Copyright (c) 2019-2023, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
** 001 CA 20190526 First version, stubs taken by converting the skeleton using FWD.
** 002 CA 20190710 Runtime implementation for some APIs.
** 003 CA 20191024 Added method support levels and updated the class support level.
** 004 CA 20200427 Fixed toLegacyString() and a fix for URI parse.
** 005 CA 20200910 Fixed QueryString property - if missing in the URI, must be empty space and not unknown.
** ME 20200912 Accumulated changes.
** 006 ME 20200925 Rewrite the 4GL way (encode/decode/parse), add missing/new methods as per OE12.2.
** ME 20210111 Only add fragment part to encode if not null or empty.
** CA 20210113 Renamed clear() to clear_(), to follow NameConverter's rules.
** ME 20210205 Use a StringStringMap variable to access the internal map, use longchar for put method.
** 007 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy signature.
** OM 20210323 Reworked I18n in LOB implementation.
** VVT 20210917 Javadoc fixed.
** OM 20210917 Code maintenance.
** CA 20220120 Do not used TypeFactory.object when the OO reference must not be tracked or registered.
** Do not use TypeFactory.object for internal usages, use ObjectVar if the reference must
** be tracked.
** All TypeFactory.object variable definitions must be done outside of the top-level block.
** ME 20210929 Fix return object on parse/resolveRelativeReference.
** CA 20220923 Variable definitions (including associated with parameters) must be done always outside of
** the BlockManager API
** 008 HC 20230918 Changed fragment part of URI to be decoded if _p2 of parse holds true (#7653-6).
** 009 CA 20231113 The 'execute' method must be annotated with LegacySignature Type.Execute, and also can be
** dropped if is a no-op.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.oo.net;
import com.goldencode.p2j.util.*;
import com.goldencode.p2j.util.BlockManager.Action;
import com.goldencode.p2j.util.BlockManager.Condition;
import com.goldencode.p2j.oo.core.Assert;
import com.goldencode.p2j.oo.core.LegacyString;
import com.goldencode.p2j.oo.core.collections.StringStringMap;
import com.goldencode.p2j.oo.lang.*;
import com.goldencode.p2j.security.ContextLocal;
import static com.goldencode.p2j.util.ArrayAssigner.assignSingle;
import static com.goldencode.p2j.util.BlockManager.*;
import static com.goldencode.p2j.report.ReportConstants.*;
import static com.goldencode.p2j.util.InternalEntry.Type;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* Business logic (converted to Java from the 4GL source code in
* OpenEdge/Net/URI.cls).
*/
@LegacyResource(resource = "OpenEdge.Net.URI")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_PARTIAL)
public class Uri extends BaseObject
{
private static final int _schemePartIdx = 0;
private static final int _authorityPartIdx = 1;
private static final int _pathPartIdx = 2;
private static final int _queryPartIdx = 3;
private static final int _fragmentPartIdx = 4;
@LegacySignature(type = Type.PROPERTY, name = "SCHEME_PART_IDX")
private static ContextLocal<integer> schemePartIdx = new ContextLocal<integer>()
{
protected integer initialValue()
{
return TypeFactory.integer(Long.valueOf(_schemePartIdx + 1));
}
};
@LegacySignature(type = Type.PROPERTY, name = "AUTHORITY_PART_IDX")
private static ContextLocal<integer> authorityPartIdx = new ContextLocal<integer>()
{
protected integer initialValue()
{
return TypeFactory.integer(Long.valueOf(_authorityPartIdx + 1));
}
};
@LegacySignature(type = Type.PROPERTY, name = "PATH_PART_IDX")
private static ContextLocal<integer> pathPartIdx = new ContextLocal<integer>()
{
protected integer initialValue()
{
return TypeFactory.integer(Long.valueOf(_pathPartIdx + 1));
}
};
@LegacySignature(type = Type.PROPERTY, name = "QUERY_PART_IDX")
private static ContextLocal<integer> queryPartIdx = new ContextLocal<integer>()
{
protected integer initialValue()
{
return TypeFactory.integer(Long.valueOf(_queryPartIdx + 1));
}
};
@LegacySignature(type = Type.PROPERTY, name = "FRAGMENT_PART_IDX")
private static ContextLocal<integer> fragmentPartIdx = new ContextLocal<integer>()
{
protected integer initialValue()
{
return TypeFactory.integer(Long.valueOf(_fragmentPartIdx + 1));
}
};
private static BitSet defaultAllowed;
private static BitSet schemeAllowed;
private static BitSet userinfoAllowed;
private static BitSet hostAllowed;
private static BitSet pathAllowed;
private static BitSet queryAllowed;
private static BitSet fragmentAllowed;
private static BitSet cookieAllowed;
private static BitSet localAllowed;
private enum HostType
{
NAME, IPV4, IPLIT
}
private character fragment = TypeFactory.character();
private character host = TypeFactory.character();
private HostType hostType;
// default unknown
private character password = new character();
private character path = TypeFactory.character();
// default unknown
private integer port = new integer();
private object<? extends com.goldencode.p2j.oo.core.collections.StringStringMap> queryMap = TypeFactory
.object(com.goldencode.p2j.oo.core.collections.StringStringMap.class);
private character scheme = TypeFactory.character();
// default unknown
private character user = new character();
// encoding, from java.net.URI
private final static char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F' };
@LegacySignature(returns = "INTEGER", type = Type.GETTER, name = "SCHEME_PART_IDX")
public static integer getSchemePartIdx()
{
return function(Uri.class, "SCHEME_PART_IDX", integer.class, new Block((Body) () -> {
returnNormal(schemePartIdx.get());
}));
}
@LegacySignature(returns = "INTEGER", type = Type.GETTER, name = "AUTHORITY_PART_IDX")
public static integer getAuthorityPartIdx()
{
return function(Uri.class, "AUTHORITY_PART_IDX", integer.class, new Block((Body) () -> {
returnNormal(authorityPartIdx.get());
}));
}
@LegacySignature(returns = "INTEGER", type = Type.GETTER, name = "PATH_PART_IDX")
public static integer getPathPartIdx()
{
return function(Uri.class, "PATH_PART_IDX", integer.class, new Block((Body) () -> {
returnNormal(pathPartIdx.get());
}));
}
@LegacySignature(returns = "INTEGER", type = Type.GETTER, name = "QUERY_PART_IDX")
public static integer getQueryPartIdx()
{
return function(Uri.class, "QUERY_PART_IDX", integer.class, new Block((Body) () -> {
returnNormal(queryPartIdx.get());
}));
}
@LegacySignature(returns = "INTEGER", type = Type.GETTER, name = "FRAGMENT_PART_IDX")
public static integer getFragmentPartIdx()
{
return function(Uri.class, "FRAGMENT_PART_IDX", integer.class, new Block((Body) () -> {
returnNormal(fragmentPartIdx.get());
}));
}
private static void _appendEscape(StringBuffer sb, byte b)
{
sb.append('%');
sb.append(hexDigits[(b >> 4) & 0x0f]);
sb.append(hexDigits[(b >> 0) & 0x0f]);
}
private static void _appendEncoded(StringBuffer sb, char c)
{
ByteBuffer bb = null;
try
{
bb = StandardCharsets.UTF_8.newEncoder().encode(CharBuffer.wrap("" + c));
}
catch (CharacterCodingException x)
{
assert false;
}
while (bb.hasRemaining())
{
int b = bb.get() & 0xff;
_appendEscape(sb, (byte) b);
}
}
static
{
defaultAllowed = new BitSet(128);
schemeAllowed = new BitSet(128);
userinfoAllowed = new BitSet(128);
hostAllowed = new BitSet(128);
pathAllowed = new BitSet(128);
queryAllowed = new BitSet(128);
fragmentAllowed = new BitSet(128);
cookieAllowed = new BitSet(128);
localAllowed = new BitSet(128);
int i;
for (i = 'a'; i <= 'z'; i++)
{
localAllowed.set(i);
}
for (i = 'A'; i <= 'Z'; i++)
{
localAllowed.set(i);
}
for (i = '0'; i <= '9'; i++)
{
localAllowed.set(i);
}
schemeAllowed.or(localAllowed);
// [-,.,_,~]
localAllowed.set('-');
localAllowed.set('_');
localAllowed.set('.');
localAllowed.set('~');
defaultAllowed.or(localAllowed);
// [!,$,&,',(,),*,+]+[,]+[;]+[=]
localAllowed.set('!');
localAllowed.set('$');
localAllowed.set('&');
localAllowed.set(39);
localAllowed.set('(');
localAllowed.set(')');
localAllowed.set('*');
localAllowed.set('+');
localAllowed.set(',');
localAllowed.set(';');
localAllowed.set('=');
hostAllowed.or(localAllowed);
userinfoAllowed.or(localAllowed);
// [:,@]
localAllowed.set(':');
localAllowed.set('@');
pathAllowed.or(localAllowed);
queryAllowed.or(localAllowed);
fragmentAllowed.or(localAllowed);
// final touch on scheme - add [+,-,.]
schemeAllowed.set('+');
schemeAllowed.set('-');
schemeAllowed.set('.');
// final touch on userInfo - add [:]
userinfoAllowed.set(':');
// final touch on query - add [/,?] remove [&]
queryAllowed.set('/');
queryAllowed.set('?');
queryAllowed.clear('&');
// TODO: in OE 1.7, queryAllowed must contain '=' char; in OE 1.6, '=' gets encoded.
// final touch on fragment - add [/,?]
fragmentAllowed.set('/');
fragmentAllowed.set('?');
// cookie
for (i = 33; i <= 126; i++)
{
cookieAllowed.set(i);
}
cookieAllowed.clear(34);
cookieAllowed.clear(44);
cookieAllowed.clear(59);
cookieAllowed.clear(92);
}
@LegacySignature(type = Type.EXECUTE)
public void __net_Uri_execute__()
{
onBlockLevel(Condition.ERROR, Action.THROW);
}
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "BaseURI")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public character getBaseUri()
{
return function(this, "BaseURI", character.class, new Block((Body) () -> {
returnNormal(new character(_getBaseUri()));
}));
}
public String _getBaseUri()
{
StringBuilder sb = new StringBuilder(getScheme().getValue()).append("://");
sb.append(_getAuthority());
return sb.toString();
}
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "Fragment")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public character getFragment()
{
return function(this, "Fragment", character.class, new Block((Body) () -> {
returnNormal(fragment);
}));
}
@LegacySignature(type = Type.SETTER, name = "Fragment", parameters = {
@LegacyParameter(name = "_var", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | CVT_LVL_FULL)
public void setFragment(final character _var)
{
character var = TypeFactory.initInput(_var);
internalProcedure(Uri.class, this, "Fragment", new Block((Body) () ->
{
fragment.assign(var.isUnknown() ? "" : var);
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "Host")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public character getHost()
{
return function(this, "Host", character.class, new Block((Body) () -> {
returnNormal(host);
}));
}
protected String _getAuthority() {
StringBuilder sb = new StringBuilder();
if (!user.isUnknown())
{
sb.append(user.getValue());
if (!password.isUnknown())
{
sb.append(":").append(getPassword().getValue());
}
sb.append("@");
}
if (hostType.equals(HostType.IPLIT))
{
sb.append("[").append(host.getValue()).append("]");
}
else
{
sb.append(host.getValue());
}
if (!port.isUnknown())
{
sb.append(":").append(getPort().intValue());
}
return sb.toString();
}
private void _setHost(final character host)
{
if (host.getValue().startsWith("["))
{
this.hostType = HostType.IPLIT;
int endPos = host.getValue().indexOf("]");
// 4gl implementation doesn't enforce closing element,
// it actually trims everything after first closing bracket
this.host.assign(_substring(host.getValue(), 1, endPos));
}
else
{
this.hostType = HostType.NAME;
this.host.assign(host);
}
}
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "Password")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public character getPassword()
{
return function(this, "Password", character.class, new Block((Body) () -> {
returnNormal(password);
}));
}
@LegacySignature(type = Type.SETTER, name = "Password", parameters = {
@LegacyParameter(name = "_var", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | CVT_LVL_FULL)
public void setPassword(final character _var)
{
character var = TypeFactory.initInput(_var);
internalProcedure(Uri.class, this, "Password", new Block((Body) () ->
{
password.assign(var);
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "Path")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public character getPath()
{
return function(this, "Path", character.class, new Block((Body) () -> {
returnNormal(path);
}));
}
@LegacySignature(type = Type.SETTER, name = "Path", parameters = {
@LegacyParameter(name = "_pPath", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void setPath(final character _pPath)
{
character pPath = TypeFactory.initInput(_pPath);
internalProcedure(Uri.class, this, "Path", new Block((Body) () ->
{
Assert.notNullOrEmpty(pPath, new character("path"));
// no validation nor encoding, this is probably a bug and should just call protected setPath method instead
String newPath = pPath.getValue();
if (!newPath.startsWith("/"))
newPath = "/" + newPath;
path.assign(newPath);
}));
}
@LegacySignature(type = Type.METHOD, name = "SetPath", parameters = {
@LegacyParameter(name = "_pPath", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
protected void setPath_1(final character _pPath)
{
character pPath = TypeFactory.initInput(_pPath);
internalProcedure(Uri.class, this, "SetPath", new Block((Body) () ->
{
String newPath = pPath.isUnknown() ? "/" : pPath.getValue();
if (!newPath.startsWith("/"))
newPath = "/" + newPath;
if (newPath.length() > 1) {
String[] entries = TextOps.entries(newPath, "/");
newPath = Arrays.stream(entries).map(p -> _encodeString(p, null, pathAllowed, false)).collect(Collectors.joining("/"));
}
setPath(new character(newPath));
}));
}
@LegacySignature(returns = "INTEGER", type = Type.GETTER, name = "Port")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public integer getPort()
{
return function(this, "Port", integer.class, new Block((Body) () -> {
returnNormal(port);
}));
}
@LegacySignature(type = Type.SETTER, name = "Port", parameters = {
@LegacyParameter(name = "_var", type = "INTEGER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void setPort(final integer _var)
{
integer var = TypeFactory.initInput(_var);
internalProcedure(Uri.class, this, "Port", new Block((Body) () ->
{
port.assign(var);
}));
}
@LegacySignature(returns = "OBJECT", qualified = "OpenEdge.Core.Collections.IStringStringMap", type = Type.GETTER, name = "QueryMap")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
protected object<? extends com.goldencode.p2j.oo.core.collections.IStringStringMap> getQueryMap()
{
return function(this, "QueryMap", object.class, new Block((Body) () -> {
if (queryMap.isUnknown())
queryMap.assign(ObjectOps.newInstance(StringStringMap.class));
returnNormal(queryMap);
}));
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetQueryMap", qualified = "openedge.core.collections.istringstringmap")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public object<? extends com.goldencode.p2j.oo.core.collections.IStringStringMap> getQueryMap_1()
{
return function(this, "GetQueryMap", object.class, new Block((Body) () -> {
returnNormal(ObjectOps.newInstance(StringStringMap.class, "I", getQueryMap()));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "QueryString")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public character getQueryString()
{
return function(this, "QueryString", character.class, new Block((Body) () -> {
returnNormal(new character(_getQueryString(false)));
}));
}
private void _setQueryString(final character var)
{
getQueryMap().ref().clear_();
addQueryString(var, new logical(true));
}
public String _getQueryString(boolean encode)
{
if (queryMap.isUnknown() || queryMap.ref().isEmpty().booleanValue())
{
return "";
}
String qryString = queryMap.ref().getMap().entrySet().stream().map(e -> {
LegacyString key = (LegacyString) e.getKey().ref();
LegacyString value = (LegacyString) e.getValue().ref();
String qry = key.toLegacyString().getValue();
if (!value.getValue().isUnknown())
qry += "=";
if (!value.isNullOrEmpty().booleanValue())
qry += value.toLegacyString().getValue();
return encode ? encodeQuery(qry) : qry;
}).reduce("?", (s, e) -> {
if ("?".equals(s))
return s + e;
return s + "&" + e;
});
return qryString;
}
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "RelativeURI")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public character getRelativeUri()
{
return function(this, "RelativeURI", character.class, new Block((Body) () -> {
returnNormal(new character(_getRelativeUri()));
}));
}
public String _getRelativeUri()
{
StringBuilder sb = new StringBuilder(path.getValue());
String extra = _getQueryString(false);
if (!extra.isEmpty())
sb.append(extra);
extra = getFragment().getValue();
if (extra != null && !extra.isEmpty())
sb.append("#").append(extra);
return sb.toString();
}
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "Scheme")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public character getScheme()
{
return function(this, "Scheme", character.class, new Block((Body) () -> {
returnNormal(scheme);
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "User")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public character getUser()
{
return function(this, "User", character.class, new Block((Body) () -> {
returnNormal(user);
}));
}
@LegacySignature(type = Type.SETTER, name = "User", parameters = {
@LegacyParameter(name = "_var", type = "CHARACTER", mode = "INPUT") })
public void setUser(final character _var)
{
character var = TypeFactory.initInput(_var);
internalProcedure(Uri.class, this, "User", new Block((Body) () ->
{
user.assign(var);
}));
}
@LegacySignature(type = Type.CONSTRUCTOR)
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static void __net_Uri_constructor__static__()
{
externalProcedure(Uri.class, new Block((Body) () ->
{
onBlockLevel(Condition.ERROR, Action.THROW);
}));
}
@LegacySignature(type = Type.CONSTRUCTOR, parameters = {
@LegacyParameter(name = "host", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public void __net_Uri_constructor__(final character _host)
{
__net_Uri_constructor__(UriSchemeEnum.http.ref().toLegacyString(), _host);
}
@LegacySignature(type = Type.CONSTRUCTOR, parameters = {
@LegacyParameter(name = "scheme", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "host", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public void __net_Uri_constructor__(final character _scheme, final character _host)
{
__net_Uri_constructor__(_scheme, _host, new integer());
}
@LegacySignature(type = Type.CONSTRUCTOR, parameters = {
@LegacyParameter(name = "scheme", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "host", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "port", type = "INTEGER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public void __net_Uri_constructor__(final character _scheme, final character _host,
final integer _port)
{
__net_Uri_constructor__(_scheme, _host, _port, new character("/"),
ObjectOps.newInstance(StringStringMap.class), TypeFactory.character());
}
@LegacySignature(type = Type.CONSTRUCTOR, parameters = {
@LegacyParameter(name = "scheme", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "host", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "port", type = "INTEGER", mode = "INPUT"),
@LegacyParameter(name = "path", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "query", type = "OBJECT", qualified = "openedge.core.collections.istringstringmap", mode = "INPUT"),
@LegacyParameter(name = "fragment", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void __net_Uri_constructor__(final character _scheme, final character _host,
final integer _port, final character _path,
final object<? extends com.goldencode.p2j.oo.core.collections.IStringStringMap> _query,
final character _fragment)
{
character scheme = TypeFactory.initInput(_scheme);
character host = TypeFactory.initInput(_host);
integer port = TypeFactory.initInput(_port);
character path = TypeFactory.initInput(_path);
object<? extends com.goldencode.p2j.oo.core.collections.IStringStringMap> query = TypeFactory
.initInput(_query);
character fragment = TypeFactory.initInput(_fragment);
internalProcedure(this, "__net_Uri_constructor__", new Block((Body) () -> {
__lang_BaseObject_constructor__();
Assert.notNullOrEmpty(scheme, new character("Scheme"));
Assert.notNullOrEmpty(host, new character("Host"));
Assert.notNull(query, new character("QueryMap"));
Assert.notNullOrEmpty(path, new character("Path"));
this.scheme.assign(scheme);
this.queryMap.assign(query);
_setHost(host);
setPort(port);
setPath(path);
setFragment(fragment);
}));
}
@LegacySignature(type = Type.CONSTRUCTOR, parameters = {
@LegacyParameter(name = "pScheme", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "pAuthority", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "pPath", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "pQuery", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "pFragment", type = "CHARACTER", mode = "INPUT") })
public void __net_Uri_constructor__(final character pScheme, final character pAuthority,
final character pPath, final character pQuery, final character pFragment)
{
character scheme = TypeFactory.initInput(pScheme);
character authority = TypeFactory.initInput(pAuthority);
character path = TypeFactory.initInput(pPath);
character fragment = TypeFactory.initInput(pFragment);
character query = TypeFactory.initInput(pQuery);
internalProcedure(this, "__net_Uri_constructor__", new Block((Body) () -> {
__lang_BaseObject_constructor__();
Assert.notNullOrEmpty(scheme, new character("URI Scheme"));
this.scheme.assign(scheme);
setAuthority(authority, new logical(true));
setPath_1(path);
addQueryString(query, new logical(true));
setFragment(fragment);
}));
}
@LegacySignature(type = Type.METHOD, name = "AddPathSegment", parameters = {
@LegacyParameter(name = "_p1", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | CVT_LVL_FULL)
public void addPathSegment(final character _p1)
{
character p1 = TypeFactory.initInput(_p1);
internalProcedure(this, "AddPathSegment", new Block((Body) () -> {
character seg = encodePath(p1);
if (!seg.getValue().trim().isEmpty())
{
if (this.path.getValue().endsWith("/"))
this.path.assign(this.path.getValue() + seg.getValue());
else
this.path.assign(this.path.getValue() + "/" + seg.getValue());
}
}));
}
@LegacySignature(type = Type.METHOD, name = "AddQuery", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void addQuery(final character _p1)
{
addQuery(_p1, new character());
}
@LegacySignature(type = Type.METHOD, name = "AddQuery", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "p2", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void addQuery(final character _p1, final character _p2)
{
character p1 = TypeFactory.initInput(_p1);
character p2 = TypeFactory.initInput(_p2);
internalProcedure(this, "AddQuery", new Block((Body) () -> {
Assert.notNullOrEmpty(p1, new character("Query name"));
getQueryMap().ref().put(p1, p2.isUnknown() ? new longchar() : new longchar(encodeQuery(p2)));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "Decode")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public character decode()
{
return function(this, "Decode", character.class, new Block((Body) () -> {
returnNormal(decode(this.toLegacyString()));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "Decode", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character decode(final character p1)
{
return function(Uri.class, "Decode", character.class, new Block((Body) () -> {
returnNormal(decode(p1, new character()));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "Decode", parameters = {
@LegacyParameter(name = "data", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "encoding", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character decode(final character _data, final character _encoding)
{
return function(Uri.class, "Decode", character.class, new Block((Body) () -> {
String data = _data.isUnknown() ? "" : _data.toStringMessage();
String encoding = _encoding.isUnknown() ? "" : _encoding.getValue().trim();
if (data.trim().isEmpty())
{
returnNormal(_data);
}
try
{
String uri = _decode(data);
// in FWD, the [character] always uses the internal CP, CP conversion is a NOP
returnNormal(new character(uri));
}
catch (UnsupportedEncodingException e)
{
undoThrow(AppError.newInstance(String.format("Cannot decode malformed string %s", data), 0));
}
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "Decode", parameters = {
@LegacyParameter(name = "p1", type = "OBJECT", qualified = "openedge.core.string", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public static character decode(
final object<? extends com.goldencode.p2j.oo.core.LegacyString> _p1)
{
object<? extends com.goldencode.p2j.oo.core.LegacyString> p1 = TypeFactory.initInput(_p1);
return function(Uri.class, "Decode", character.class, new Block((Body) () -> {
returnNormal(decode(p1.ref().toLegacyString(), p1.ref().getEncoding()));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "Encode")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public character encode()
{
return function(this, "Encode", character.class, new Block((Body) () -> {
returnNormal(encodeUri(new object(this)));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "Encode", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "p2", type = "OBJECT", qualified = "openedge.net.uriencodingtypeenum", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public static character encode(final character p1,
final object<? extends com.goldencode.p2j.oo.net.UriEncodingTypeEnum> p2)
{
return encode(p1, p2, new logical(false));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "Encode", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "p2", type = "OBJECT", qualified = "openedge.net.uriencodingtypeenum", mode = "INPUT"),
@LegacyParameter(name = "p3", type = "LOGICAL", mode = "INPUT") })
public static character encode(final character _p1,
final object<? extends com.goldencode.p2j.oo.net.UriEncodingTypeEnum> _p2,
final logical _p3)
{
character p1 = TypeFactory.initInput(_p1);
object<? extends com.goldencode.p2j.oo.net.UriEncodingTypeEnum> p2 = TypeFactory
.initInput(_p2);
logical reEncode = TypeFactory.initInput(_p3);
return function(Uri.class, "Encode", character.class, new Block((Body) () -> {
if (p1.isUnknown() || p1.getValue().trim().isEmpty())
returnNormal(new character(""));
if (reEncode.isUnknown())
{
reEncode.assign(true);
}
if (!p2._isValid())
{
returnNormal(new character(
_encodeString(p1.getValue(), "?", defaultAllowed, reEncode.booleanValue())));
}
switch (p2.ref().getValue().toString())
{
case "1":
//scheme
returnNormal(new character(_encodeString(p1.getValue(), "?", schemeAllowed,
reEncode.booleanValue())));
case "2":
//host
returnNormal(new character(
_encodeString(p1.getValue(), "?", hostAllowed, reEncode.booleanValue())));
case "3":
//path
returnNormal(new character(
_encodeString(p1.getValue(), "?", pathAllowed, reEncode.booleanValue())));
case "4":
//query
returnNormal(new character(
_encodeString(p1.getValue(), "?", queryAllowed, reEncode.booleanValue())));
case "5":
//fragment
returnNormal(new character(_encodeString(p1.getValue(), "?", fragmentAllowed,
reEncode.booleanValue())));
case "6":
//cookie
returnNormal(new character(_encodeString(p1.getValue(), "?", cookieAllowed,
reEncode.booleanValue())));
default:
returnNormal(new character(_encodeString(p1.getValue(), "?", defaultAllowed,
reEncode.booleanValue())));
}
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "Encode", parameters = {
@LegacyParameter(name = "p1", type = "OBJECT", qualified = "openedge.net.uri", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public static character encode(final object<? extends com.goldencode.p2j.oo.net.Uri> p1)
{
return encodeUri(p1);
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeCookie", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character encodeCookie(final character _p1)
{
character p1 = TypeFactory.initInput(_p1);
return function(Uri.class, "EncodeCookie", character.class, new Block((Body) () -> {
returnNormal(_encodeString(p1.getValue(), "?", cookieAllowed, false));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeCookie", parameters = {
@LegacyParameter(name = "p1", type = "OBJECT", qualified = "openedge.core.string", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character encodeCookie(
final object<? extends com.goldencode.p2j.oo.core.LegacyString> _p1)
{
object<? extends com.goldencode.p2j.oo.core.LegacyString> p1 = TypeFactory.initInput(_p1);
return function(Uri.class, "EncodeCookie", character.class, new Block((Body) () -> {
returnNormal(encodeCookie(p1.ref().toLegacyString().getValue(),
p1.ref().getEncoding().getValue()));
}));
}
public static String encodeCookie(final String data, final String codePage)
{
return _encodeString(data, codePage, cookieAllowed, false);
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeFragment")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public character encodeFragment()
{
return function(this, "EncodeFragment", character.class, new Block((Body) () -> {
returnNormal(encodeFragment(this.fragment));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeFragment", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character encodeFragment(final character _p1)
{
character p1 = TypeFactory.initInput(_p1);
return function(Uri.class, "EncodeFragment", character.class, new Block((Body) () -> {
returnNormal(_encodeString(p1.getValue(), "?", fragmentAllowed, false));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeFragment", parameters = {
@LegacyParameter(name = "p1", type = "OBJECT", qualified = "openedge.core.string", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character encodeFragment(
final object<? extends com.goldencode.p2j.oo.core.LegacyString> _p1)
{
object<? extends com.goldencode.p2j.oo.core.LegacyString> p1 = TypeFactory.initInput(_p1);
return function(Uri.class, "EncodeFragment", character.class, new Block((Body) () -> {
returnNormal(_encodeString(p1.ref().toLegacyString().getValue(),
p1.ref().getEncoding().getValue(), fragmentAllowed, false));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeHost")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public character encodeHost()
{
return function(this, "EncodeHost", character.class, new Block((Body) () -> {
if (this.hostType == HostType.IPLIT)
{
returnNormal(new character("[" + encodeHost(this.host).getValue() + "]"));
}
else
returnNormal(encodeHost(this.host));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeHost", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character encodeHost(final character _p1)
{
character p1 = TypeFactory.initInput(_p1);
return function(Uri.class, "EncodeHost", character.class, new Block((Body) () -> {
returnNormal(_encodeString(p1.getValue(), "?", hostAllowed, false));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeHost", parameters = {
@LegacyParameter(name = "p1", type = "OBJECT", qualified = "openedge.core.string", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character encodeHost(
final object<? extends com.goldencode.p2j.oo.core.LegacyString> _p1)
{
object<? extends com.goldencode.p2j.oo.core.LegacyString> p1 = TypeFactory.initInput(_p1);
return function(Uri.class, "EncodeHost", character.class, new Block((Body) () -> {
returnNormal(_encodeString(p1.ref().toLegacyString().getValue(),
p1.ref().getEncoding().getValue(), hostAllowed, false));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodePath", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character encodePath(final character p1)
{
return new character(_encodeString(p1.getValue(), "?", pathAllowed, false));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodePath", parameters = {
@LegacyParameter(name = "p1", type = "OBJECT", qualified = "openedge.core.string", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character encodePath(
final object<? extends com.goldencode.p2j.oo.core.LegacyString> _p1)
{
object<? extends com.goldencode.p2j.oo.core.LegacyString> p1 = TypeFactory.initInput(_p1);
return function(Uri.class, "EncodePath", character.class, new Block((Body) () -> {
returnNormal(_encodeString(p1.ref().toLegacyString().getValue(),
p1.ref().getEncoding().getValue(), pathAllowed, false));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeQuery")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public character encodeQuery()
{
return function(this, "EncodeQuery", character.class, new Block((Body) () -> {
returnNormal(encodeQuery_1(new object(this)));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeQuery", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character encodeQuery(final character _p1)
{
character p1 = TypeFactory.initInput(_p1);
return function(Uri.class, "EncodeQuery", character.class, new Block((Body) () -> {
returnNormal(encodeQuery(p1.getValue()));
}));
}
public static String encodeQuery(String query) {
return _encodeString(query, "?", queryAllowed, false);
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeQuery", parameters = {
@LegacyParameter(name = "p1", type = "OBJECT", qualified = "openedge.core.string", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character encodeQuery(
final object<? extends com.goldencode.p2j.oo.core.LegacyString> _p1)
{
object<? extends com.goldencode.p2j.oo.core.LegacyString> p1 = TypeFactory.initInput(_p1);
return function(Uri.class, "EncodeQuery", character.class, new Block((Body) () -> {
returnNormal(_encodeString(p1.ref().toLegacyString().getValue(),
p1.ref().getEncoding().getValue(), queryAllowed, false));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeQuery", parameters = {
@LegacyParameter(name = "p1", type = "OBJECT", qualified = "openedge.net.uri", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character encodeQuery_1(
final object<? extends com.goldencode.p2j.oo.net.Uri> _p1)
{
object<? extends com.goldencode.p2j.oo.net.Uri> p1 = TypeFactory.initInput(_p1);
return function(Uri.class, "EncodeQuery", character.class, new Block((Body) () -> {
Assert.notNull(p1, new character("URI"));
returnNormal(p1.ref()._getQueryString(true));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeScheme")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public character encodeScheme()
{
return encodeScheme(this.scheme);
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeScheme", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character encodeScheme(final character _p1)
{
character p1 = TypeFactory.initInput(_p1);
return function(Uri.class, "EncodeScheme", character.class, new Block((Body) () -> {
returnNormal(_encodeString(p1.getValue(), "?", schemeAllowed, false));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeScheme", parameters = {
@LegacyParameter(name = "p1", type = "OBJECT", qualified = "openedge.core.string", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character encodeScheme(
final object<? extends com.goldencode.p2j.oo.core.LegacyString> _p1)
{
object<? extends com.goldencode.p2j.oo.core.LegacyString> p1 = TypeFactory.initInput(_p1);
return function(Uri.class, "EncodeScheme", character.class, new Block((Body) () -> {
returnNormal(_encodeString(p1.ref().toLegacyString().getValue(),
p1.ref().getEncoding().getValue(), schemeAllowed, false));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeString", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character encodeString(final character _p1)
{
character p1 = TypeFactory.initInput(_p1);
return function(Uri.class, "EncodeString", character.class, new Block((Body) () -> {
returnNormal(_encodeString(p1.getValue(), "?", defaultAllowed, false));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeString", parameters = {
@LegacyParameter(name = "p1", type = "OBJECT", qualified = "openedge.core.string", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character encodeString(
final object<? extends com.goldencode.p2j.oo.core.LegacyString> _p1)
{
object<? extends com.goldencode.p2j.oo.core.LegacyString> p1 = TypeFactory.initInput(_p1);
return function(Uri.class, "EncodeString", character.class, new Block((Body) () -> {
returnNormal(_encodeString(p1.ref().toLegacyString().getValue(),
p1.ref().getEncoding().getValue(), defaultAllowed, false));
}));
}
private static String _encodeString(String str, String sourceCodepage, BitSet allowed,
boolean reEncode)
{
StringBuffer sb = null;
if (str == null || str.trim().isEmpty())
return "";
if (sourceCodepage == null)
sourceCodepage = I18nOps.getCPInternal().getValue();
// TODO: codepage conversion
// str = I18nOps.codePageConvertWorker(str, sourceCodepage, "UTF-8");
for (int i = 0; i < str.length(); i++)
{
char c = str.charAt(i);
if (c == '\u0025' && !reEncode)
{
if (sb != null)
{
sb.append(str.substring(i, Math.min(i + 3, str.length())));
}
i = i + 2;
}
else if (c >= '\u007F' || c <= '\u001F' || !allowed.get(c))
{
if (sb == null)
{
sb = new StringBuffer();
sb.append(str.substring(0, i));
}
_appendEncoded(sb, c);
}
else
{
if (sb != null)
{
sb.append(c);
}
}
}
return (sb == null) ? str : sb.toString();
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeURI", parameters = {
@LegacyParameter(name = "p1", type = "OBJECT", qualified = "openedge.net.uri", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character encodeUri(final object<? extends com.goldencode.p2j.oo.net.Uri> _p1)
{
object<? extends com.goldencode.p2j.oo.net.Uri> p1 = TypeFactory.initInput(_p1);
return function(Uri.class, "EncodeURI", character.class, new Block((Body) () -> {
String encodedUri = new String();
Assert.notNull(p1, new character("URI"));
//scheme
encodedUri = encodeScheme(p1.ref().getScheme()).getValue() + "://";
//user+pass
if (!p1.ref().getUser().isUnknown())
{
encodedUri = encodedUri + p1.ref().encodeUserinfo().getValue() + "@";
}
//host
encodedUri = encodedUri + p1.ref().encodeHost().getValue();
//port
if (!p1.ref().getPort().isUnknown())
{
encodedUri = encodedUri + ":" + p1.ref().getPort().getValue();
}
//path + query
encodedUri = encodedUri + p1.ref().getPath().getValue()
+ p1.ref().encodeQuery().getValue();
//fragment
if (!TextOps.isEmpty(p1.ref().getFragment()))
{
encodedUri = encodedUri + "#" + encodeFragment(p1.ref().getFragment()).getValue();
}
returnNormal(new character(encodedUri));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeUserinfo")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public character encodeUserinfo()
{
return function(Uri.class, "EncodeUserinfo", character.class, new Block((Body) () -> {
if (this.user.isUnknown())
{
returnNormal(new character(""));
}
character u = this.getUser();
String authority = new String(_encodeString(u.getValue(), "?", hostAllowed, false));
if (!this.password.isUnknown())
{
authority = authority + ":"
+ _encodeString(this.getPassword().getValue(), "?", hostAllowed, false);
}
returnNormal(new character(authority));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeUserinfo", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character encodeUserinfo(final character _p1)
{
character p1 = TypeFactory.initInput(_p1);
return function(Uri.class, "EncodeUserinfo", character.class, new Block((Body) () -> {
returnNormal(_encodeString(p1.getValue(), "?", userinfoAllowed, false));
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "EncodeUserinfo", parameters = {
@LegacyParameter(name = "p1", type = "OBJECT", qualified = "openedge.core.string", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character encodeUserinfo(
final object<? extends com.goldencode.p2j.oo.core.LegacyString> _p1)
{
object<? extends com.goldencode.p2j.oo.core.LegacyString> p1 = TypeFactory.initInput(_p1);
return function(Uri.class, "EncodeUserinfo", character.class, new Block((Body) () -> {
returnNormal(_encodeString(p1.ref().toLegacyString().getValue(),
p1.ref().getEncoding().getValue(), userinfoAllowed, false));
}));
}
@LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "GetPathSegments", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", extent = -1, mode = "OUTPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public integer getPathSegments(final OutputExtentParameter<character> extp1)
{
character[][] p1 = { TypeFactory.initOutput(extp1) };
return function(this, "GetPathSegments", integer.class, new Block((Body) () -> {
String[] entries = TextOps.entries(this.path.getValue(), "/");
p1[0] = ArrayAssigner.resize(p1[0], entries.length - 1);
for (int i = 0; i < p1[0].length; i++)
{
assignSingle(p1[0], i + 1, decode(new character(entries[i + 1])));
}
returnNormal(p1[0].length);
}));
}
@LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "GetQueryNames", parameters = {
@LegacyParameter(name = "np1", type = "CHARACTER", extent = -1, mode = "OUTPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public integer getQueryNames(final OutputExtentParameter<character> extnp1)
{
character[][] np1 = { TypeFactory.initOutput(extnp1) };
return function(this, "GetQueryNames", integer.class, new Block((Body) () -> {
if (queryMap.isUnknown())
{
returnNormal(0);
}
else
{
Set<object> keys = this.queryMap.ref().getMap().keySet();
if (!keys.isEmpty())
{
np1[0] = ArrayAssigner.resize(np1[0], keys.size());
int idx = 0;
for (object key : keys)
{
assignSingle(np1[0], ++idx, key.toString());
}
}
returnNormal(keys.size());
}
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "GetQueryValue", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public character getQueryValue(final character _p1)
{
character p1 = TypeFactory.initInput(_p1);
return function(this, "GetQueryValue", character.class, new Block((Body) () -> {
Assert.notNullOrEmpty(p1, new character("Query name"));
if (queryMap.isUnknown())
{
returnNormal(new character());
}
else
{
returnNormal(new character(this.queryMap.ref().get(p1)));
}
}));
}
@LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "HasQueryName", parameters = {
@LegacyParameter(name = "pName", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public logical hasQueryName(final character pName)
{
return function(Uri.class, this, "HasQueryName", logical.class, new Block((Body) () -> {
returnNormal(!queryMap.isUnknown() && queryMap.ref().containsKey(pName).booleanValue());
}));
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "Parse", qualified = "openedge.net.uri", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_BASIC)
public static object<? extends com.goldencode.p2j.oo.net.Uri> parse(final character _p1)
{
return parse(_p1, new logical(true));
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "Parse", qualified = "openedge.net.uri", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "p2", type = "LOGICAL", mode = "INPUT") })
public static object<? extends Uri> parse(final character _p1, final logical _p2)
{
character p1 = TypeFactory.initInput(_p1);
logical p2 = TypeFactory.initInput(_p2);
object<Uri> obj = TypeFactory.object(Uri.class);
return function(Uri.class, "Parse", object.class, new Block((Body) () -> {
Assert.notNullOrEmpty(p1, new character("URI string"));
character[] relative = splitUri(p1);
// defaults to HTTP
if (TextOps.isEmpty(relative[_schemePartIdx]))
relative[_schemePartIdx] = UriSchemeEnum.http.ref().toLegacyString();
// check authority as this is mandatory
if (TextOps.isEmpty(relative[_authorityPartIdx]))
undoThrow(AppError.newInstance(TextOps.substitute("Unable to parse malformed URI: &1", p1), new integer(0)));
// authority seems to always be decoded
relative[_authorityPartIdx] = decode(relative[_authorityPartIdx]);
// unless false decode by default schema, path, query, fragment
if (p2.isUnknown() || p2.booleanValue())
{
relative[_schemePartIdx] = decode(relative[_schemePartIdx]);
relative[_pathPartIdx] = decode(relative[_pathPartIdx]);
relative[_fragmentPartIdx] = decode(relative[_fragmentPartIdx]);
relative[_queryPartIdx] = decode(relative[_queryPartIdx]);
obj.assign(ObjectOps.newInstance(Uri.class, "IIIII", relative[_schemePartIdx],
relative[_authorityPartIdx], relative[_pathPartIdx], relative[_queryPartIdx],
relative[_fragmentPartIdx]));
}
else
{
// don't set query string in ctor to avoid decode
obj.assign(ObjectOps.newInstance(Uri.class, "IIIII", relative[_schemePartIdx],
relative[_authorityPartIdx], relative[_pathPartIdx], "",
relative[_fragmentPartIdx]));
obj.ref().addQueryString(relative[_queryPartIdx], new logical(false));
}
returnNormal(obj);
}));
}
@LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "RemoveQuery", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public logical removeQuery(final character _p1)
{
character p1 = TypeFactory.initInput(_p1);
return function(this, "RemoveQuery", logical.class, new Block((Body) () -> {
Assert.notNullOrEmpty(p1, new character("Query name"));
boolean hasQuery = !queryMap.isUnknown() && queryMap.ref().containsKey(p1).booleanValue();
if (hasQuery)
this.queryMap.ref().remove(p1);
returnNormal(hasQuery);
}));
}
@LegacySignature(type = Type.METHOD, name = "SetPath", parameters = {
@LegacyParameter(name = "p1", type = "CHARACTER", extent = -1, mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public void setPath(final character[] _p1)
{
character[] p1 = TypeFactory.initInput(_p1);
internalProcedure(this, "SetPath", new Block((Body) () -> {
String path = new String();
String pathDelim = "";
for (character a : p1)
{
String partialSeg = encodePath(a).getValue();
if (partialSeg != "")
{
if (path == "")
{
path = "/";
}
path = path + pathDelim + partialSeg;
pathDelim = "/";
}
}
setPath(new character(path));
}));
}
@LegacySignature(type = Type.METHOD, name = "SetQueryMap", parameters = {
@LegacyParameter(name = "pQueryMap", type = "OBJECT", qualified = "openedge.core.collections.istringstringmap", mode = "INPUT") })
public void setQueryMap_1(
final object<? extends com.goldencode.p2j.oo.core.collections.IStringStringMap> _pQueryMap)
{
object<? extends com.goldencode.p2j.oo.core.collections.IStringStringMap> pQueryMap = TypeFactory
.initInput(_pQueryMap);
internalProcedure(Uri.class, this, "SetQueryMap", new Block((Body) () -> {
if (_pQueryMap._isValid() && pQueryMap.ref().getSize().getSize() > 0)
{
getQueryMap().ref().putAll_2(pQueryMap);
}
}));
}
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "ToString")
@Override
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public character toLegacyString()
{
return new character(_getBaseUri() + _getRelativeUri());
}
@LegacySignature(type = Type.METHOD, name = "SetAuthority", parameters = {
@LegacyParameter(name = "pAuthority", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "pDecode", type = "LOGICAL", mode = "INPUT") })
protected void setAuthority(final character pAuthority, final logical pDecode)
{
Assert.notNullOrEmpty(pAuthority, new character("Authority"));
String auth = pAuthority.getValue();
int idx = auth.indexOf("@");
// credentials set (user:passwd)
if (idx != -1)
{
String[] credentials = auth.substring(0, idx).split(":");
user.assign(credentials[0]);
if (credentials.length > 1)
password.assign(credentials[1]);
auth = auth.substring(idx + 1);
}
idx = auth.indexOf(":");
if (idx != -1)
{
port.assign(auth.substring(idx + 1));
auth = auth.substring(0, idx);
}
_setHost(pDecode.isUnknown() || pDecode.booleanValue() ? decode(new character(auth)) : new character(auth));
}
@LegacySignature(type = Type.METHOD, name = "AddQueryString", parameters = {
@LegacyParameter(name = "pQuery", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "pDecode", type = "LOGICAL", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public void addQueryString(character pQuery, logical pDecode)
{
if (!pQuery.isUnknown() && !pQuery.getValue().trim().isEmpty())
{
Arrays.stream(pQuery.getValue().split("&")).forEach(e -> {
String[] entries = e.split("=");
character pName = pDecode.booleanValue() ? decode(new character(entries[0])) : new character(entries[0]);
if (entries.length == 1)
addQuery(pName);
else
addQuery(pName, new character(entries[1]));
});
}
}
/**
* Parses this URI into its component parts
* [1] scheme (http/https)
* [2] authority (host:port)
* [3] path
* [4] query
* [5] fragment
* @return An array of component parts
*/
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "Split", extent = -1)
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public character[] split()
{
return extentFunction(Uri.class, this, "Split", character.class, new Block((Body) () -> {
String[] entries = new String[] {scheme.getValue(), _getAuthority(), path.getValue(), _getQueryString(false), fragment.getValue()};
if (entries[_queryPartIdx].startsWith("?"))
entries[_queryPartIdx] = entries[_queryPartIdx].substring(1);
returnExtentNormal(TypeFactory.characterExtent(entries.length, entries));
}));
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "ResolveRelativeReference", qualified = "openedge.net.uri", parameters = {
@LegacyParameter(name = "oUriBase", type = "OBJECT", qualified = "openedge.net.uri", mode = "INPUT"),
@LegacyParameter(name = "cRelativeRef", type = "CHARACTER", mode = "INPUT"), })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static object<? extends Uri> resolveRelativeReference(object<? extends Uri> oUriBase,
character cRelativeRef)
{
object<Uri> obj = TypeFactory.object(Uri.class);
return function(Uri.class, "ResolveRelativeReference", object.class,
new Block((Body) () -> {
Assert.notNull(oUriBase, new character("Base URI"));
String relRef = cRelativeRef.getValue();
character[] base = oUriBase.ref().split();
// nothing to do, return base object
if (relRef == null || relRef.trim().isEmpty())
{
// same schema, authority, path and query but fragment is reset
obj.assign(ObjectOps.newInstance(Uri.class, "IIIII",
base[_schemePartIdx], base[_authorityPartIdx],
base[_pathPartIdx], base[_queryPartIdx], ""));
}
else
{
String[] relative = _splitUri(relRef);
// no scheme, everything is considered path
if ("".equals(relative[0]))
{
// if new path is absolute
if (relative[_pathPartIdx].startsWith("/")) {
obj.assign(ObjectOps.newInstance(Uri.class, "IIIII",
base[_schemePartIdx], base[_authorityPartIdx],
relative[_pathPartIdx], relative[_queryPartIdx],
relative[_fragmentPartIdx]));
}
else {
// relative path, resolve it against current
obj.assign(ObjectOps.newInstance(Uri.class, "IIIII",
base[_schemePartIdx], base[_authorityPartIdx],
_resolvePath(base[_pathPartIdx].getValue(), relative[_pathPartIdx]),
relative[_queryPartIdx], relative[_fragmentPartIdx]));
}
}
else
{
// different scheme, complete override
obj.assign(ObjectOps.newInstance(Uri.class, "IIIII",
relative[_schemePartIdx], relative[_authorityPartIdx],
relative[_pathPartIdx], relative[_queryPartIdx],
relative[_fragmentPartIdx]));
}
}
returnNormal(obj);
}));
}
/**
* Parses an potentially-URI-containing string into URI component parts.
* This method will not fail, and will return empty values for an element of the
* URI if the string cannot be parsed or fails to parse somehow. So if the string contains
* http//example.com
* the auhority will be 'example.com' and the scheme empty (since there's no :).
* [1] scheme (http/https)
* [2] authority (host:port)
* [3] path
* [4] query
* [5] fragment
*
* @param pData the URI string
*
* @return An array of component parts
*/
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "SplitUri", extent = -1, parameters = {
@LegacyParameter(name = "pData", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
public static character[] splitUri(final character pData)
{
character[] res = TypeFactory.characterExtent();
return extentFunction(Uri.class, "SplitUri", character.class, new Block((Body) () -> {
String[] entries = _splitUri(pData.getValue());
if (entries.length == 0)
{
returnExtentNormal(res);
}
returnExtentNormal(TypeFactory.characterExtent(entries.length, entries));
}));
}
private static String[] _splitUri(final String pData) {
String[] entries = new String[] {"", "", "", "", ""};
if (pData != null && !pData.trim().isEmpty()) {
// first position seems to be used, doesn't even have to be after scheme/authority
int endIdx = pData.indexOf("#");
// fragment seems to be extracted first, everything after first # is considered fragment
if (endIdx != -1) {
entries[_fragmentPartIdx] = _substring(pData, endIdx + 1, -1);
}
// query comes second, everything between first ? and fragment separator or end of string
int qryIdx = pData.indexOf("?");
if (qryIdx != -1 && (endIdx == -1 || qryIdx < endIdx)) {
entries[_queryPartIdx] = _substring(pData, qryIdx + 1, endIdx);
endIdx = qryIdx;
}
// if no scheme/authority everything lands in path
int pathIdx = 0;
// scheme separator must be before query/fragment else is ignored
int schemaIdx = pData.indexOf(":");
if (schemaIdx > 0 && (endIdx == -1 || schemaIdx < endIdx))
{
// remaining // separator is not required for schema extraction
entries[_schemePartIdx] = _substring(pData, 0, schemaIdx++);
pathIdx = schemaIdx;
}
int authIdx = pData.indexOf("//", schemaIdx);
if (authIdx != -1 && (endIdx == -1 || authIdx < endIdx)) {
// if we have authority everything up to path/query/fragment is used
authIdx += 2;
pathIdx = pData.indexOf("/", authIdx);
entries[_authorityPartIdx] = _substring(pData, authIdx, pathIdx != -1 ? pathIdx : endIdx);
}
// everything up to query/fragment seems to land in path
if(pathIdx != -1)
entries[_pathPartIdx] = _substring(pData, pathIdx, endIdx);
}
return entries;
}
// From URLDecoder, 4GL does not throw error on incomplete or invalid escape sequence (%)
// nor does replace plus with space like Java decoder does
private static String _decode(String s) throws UnsupportedEncodingException
{
boolean needToChange = false;
int numChars = s.length();
StringBuffer sb = new StringBuffer(numChars > 500 ? numChars / 2 : numChars);
int i = 0;
char c;
while (i < numChars)
{
c = s.charAt(i);
byte[] bytes = null;
switch (c)
{
case '%':
/*
* Starting with this instance of %, process all
* consecutive substrings of the form %xy. Each
* substring %xy will yield a byte. Convert all
* consecutive bytes obtained this way to whatever
* character(s) they represent in the provided
* encoding.
*/
if (bytes == null)
bytes = new byte[4];
int pos = 0;
int expected = -1;
while (((i + 2) < numChars) && (c == '%'))
{
try
{
int v = Integer.parseInt(s.substring(i + 1, i + 3), 16);
// not an error in 4gl, left not decoded
if (v < 0)
break;
if (pos == 0)
{
if (v <= 128)
{
expected = 1;
}
// for UTF-8 only sequence start seems to be validated
// - C0-C1, F5-FF invalid
// - C2-DF (2 bytes)
// - E0-EF (3 bytes)
// - F0-F4 (4 bytes)
else if (v >= 194 && v < 245)
{
if (v < 224)
expected = 2;
else if (v < 240)
expected = 3;
else
expected = 4;
}
else
{
expected = -1;
}
}
bytes[pos++] = (byte) v;
i += 3;
if (i < numChars)
c = s.charAt(i);
// already a full sequence, reset position
if (c == '%' && pos == expected)
{
sb.append(new String(bytes, 0, pos, StandardCharsets.UTF_8));
needToChange = true;
pos = 0;
}
}
catch (NumberFormatException nfe)
{
pos = -1;
break;
}
}
// at least one escape sequence found
if (pos >= 0)
{
// last escape sequence in the list, not yet decoded
if (pos > 0)
{
if (expected != pos)
throw new UnsupportedEncodingException();
sb.append(new String(bytes, 0, pos, StandardCharsets.UTF_8));
needToChange = true;
}
break;
}
default:
sb.append(c);
i++;
break;
}
}
return (needToChange ? sb.toString() : s);
}
private static String _substring(String data, int start, int end) {
if (data == null || start < 0)
return data;
if (end == -1)
return data.substring(start);
return data.substring(start, end);
}
private static String _resolvePath(String base, String relative) {
if (!base.endsWith("/"))
base += "/";
String[] entries = TextOps.entries(base + "../" + relative, "/");
int crt = 0;
for (int i = 0; i < entries.length; i++)
{
if (".".equals(entries[i])) {
// current, ignored
} else if ("..".equals(entries[i])) {
// safeguard, can't pass up the root
if (crt > 0)
crt--;
} else {
entries[crt++] = entries[i];
}
}
return Arrays.stream(entries, 0, crt).reduce("/", (s, e) -> {
if ("/".equals(s))
return s + e;
return s + "/" + e;
});
}
}