<commit_before>/*
 * eXist-db Open Source Native XML Database
 * Copyright (C) 2001 The eXist-db Authors
 *
 * <EMAIL>
 * http://www.exist-db.org
 *
 * 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; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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.
 *
 * 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 Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.exist.xquery.util;

import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Arrays;

import org.exist.xmldb.XmldbURI;

/**
 * Utilities for URI related functions
 * 
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 */

public class URIUtils {

	private static final char[] ENCODED_ASTERISK = new char[]{'%', '2', 'A'};
	private static final char[] ENCODED_PLUS = new char[]{'%', '2', '0'};

	private final static class CharArray {
		char[] buf;
		int count;

		public CharArray(final int initalSize) {
			buf = new char[initalSize];
		}

		void append(final char c) {
			final int newcount = count + 1;
			if (newcount > buf.length) {
				buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
			}
			buf[count] = c;
			count = newcount;
		}

		public void append(final char c, final char c1) {
			final int newcount = count + 2;
			if (newcount > buf.length) {
				buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
			}
			buf[count] = c;
			buf[count+1] = c1;
			count = newcount;
		}

		public void append(final char c, final char c1, final char c2) {
			final int newcount = count + 3;
			if (newcount > buf.length) {
				buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
			}
			buf[count] = c;
			buf[count+1] = c1;
			buf[count+2] = c2;
			count = newcount;
		}

		public void append(final char[] cc) {
			final int newcount = count + cc.length;
			if (newcount > buf.length) {
				buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
			}
			System.arraycopy(cc, 0, buf, count, cc.length);
			count = newcount;
		}
	}

	/**
	 * Encodes reserved characters in a string that is intended to be used in the path segment of a URI.
	 *
	 * This function applies the URI escaping rules defined in <a href="https://www.ietf.org/rfc/rfc3986.html#section-2">RFC 3986 Section 2</a>.
	 * The effect of the function is to escape reserved characters.
	 * Each such character in the string is replaced with its percent-encoded form as described in RFC 3986.
	 *
	 * Since RFC 3986 recommends that, for consistency, URI producers and normalizers should use uppercase
	 * hexadecimal digits for all percent-encodings, this function must always generate hexadecimal values
	 * using the upper-case letters A-F.
	 *
	 * @param pathComponent the path component to URI encode.
	 *
	 * @return the URI encoded path component.
	 */
	public static String encodeForURI(final String pathComponent) {
		final String src;
		try {
			src = URLEncoder.encode(pathComponent, "UTF-8");
		} catch(final UnsupportedEncodingException e) {
			//wrap with a runtime Exception
			throw new RuntimeException(e);
		}

		final CharArray result = new CharArray(src.length());

		for (int i = 0; i < src.length(); i++) {
			final char c = src.charAt(i);

			if (c == '%') {
				if (i + 2 < src.length()) {
					final char c1 = src.charAt(i + 1);
					final char c2 = src.charAt(i + 2);

					if (c1 =='2') {
						if (c2 == 'D') {
							result.append('-');
							i += 2;
						} else if (c2 == 'E') {
							result.append('.');
							i += 2;
						} else {
							result.append(c, c1);
							i++;
						}

					} else if (c1 == '5') {
						if (c2 == 'F') {
							result.append('_');
							i += 2;
						} else {
							result.append(c, c1);
							i++;
						}

					} else if (c1 == '7') {
						if (c2 == 'E') {
							result.append('~');
							i += 2;
						} else {
							result.append(c, c1);
							i++;
						}

					} else {
						result.append(c); //TODO(AR) should this be % encoded
					}
				} else {
					result.append(c);
				}

			} else if (c == '*') {
				result.append(ENCODED_ASTERISK);

			} else if (c == '+') {
				result.append(ENCODED_PLUS);

			} else {
				result.append(c);
			}
		}

		return new String(result.buf, 0, result.count);
	}
	
	public static String iriToURI(String uriPart) {
		String result = urlEncodeUtf8(uriPart);
		result = result.replaceAll("%23", "#");
		result = result.replaceAll("%2D", "-");
		result = result.replaceAll("%5F", "_");
		result = result.replaceAll("%2E", ".");
		result = result.replaceAll("%21", "!");
		result = result.replaceAll("%7E", "~");
		result = result.replaceAll("%2A", "*");
		result = result.replaceAll("%27", "'");
		result = result.replaceAll("%28", "(");
		result = result.replaceAll("%29", ")");
		result = result.replaceAll("%3B", ";");
		result = result.replaceAll("%2F", "/");
		result = result.replaceAll("%3F", "?");		
		result = result.replaceAll("%3A", ":");
		result = result.replaceAll("%40", "@");
		result = result.replaceAll("%26", "&");
		result = result.replaceAll("%3D", "=");		
		result = result.replaceAll("%2B", "+");
		result = result.replaceAll("%24", "\\$");
		result = result.replaceAll("%2C", ",");		<commit_msg>[refactor] Round 5 of optimisations for URIUtils<commit_after>/*
 * eXist-db Open Source Native XML Database
 * Copyright (C) 2001 The eXist-db Authors
 *
 * <EMAIL>
 * http://www.exist-db.org
 *
 * 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; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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.
 *
 * 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 Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.exist.xquery.util;

import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Arrays;

import org.exist.xmldb.XmldbURI;

/**
 * Utilities for URI related functions
 * 
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 */

public class URIUtils {

	private final static class CharArray {
		char[] buf;
		int count;

		public CharArray(final int initalSize) {
			buf = new char[initalSize];
		}

		void append(final char c) {
			final int newcount = count + 1;
			if (newcount > buf.length) {
				buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
			}
			buf[count] = c;
			count = newcount;
		}

		public void append(final char c, final char c1) {
			final int newcount = count + 2;
			if (newcount > buf.length) {
				buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
			}
			buf[count] = c;
			buf[count+1] = c1;
			count = newcount;
		}

		public void append(final char c, final char c1, final char c2) {
			final int newcount = count + 3;
			if (newcount > buf.length) {
				buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
			}
			buf[count] = c;
			buf[count+1] = c1;
			buf[count+2] = c2;
			count = newcount;
		}
	}

	/**
	 * Encodes reserved characters in a string that is intended to be used in the path segment of a URI.
	 *
	 * This function applies the URI escaping rules defined in <a href="https://www.ietf.org/rfc/rfc3986.html#section-2">RFC 3986 Section 2</a>.
	 * The effect of the function is to escape reserved characters.
	 * Each such character in the string is replaced with its percent-encoded form as described in RFC 3986.
	 *
	 * Since RFC 3986 recommends that, for consistency, URI producers and normalizers should use uppercase
	 * hexadecimal digits for all percent-encodings, this function must always generate hexadecimal values
	 * using the upper-case letters A-F.
	 *
	 * @param pathComponent the path component to URI encode.
	 *
	 * @return the URI encoded path component.
	 */
	public static String encodeForURI(final String pathComponent) {
		final String src;
		try {
			src = URLEncoder.encode(pathComponent, "UTF-8");
		} catch(final UnsupportedEncodingException e) {
			//wrap with a runtime Exception
			throw new RuntimeException(e);
		}

		final CharArray result = new CharArray(src.length());

		for (int i = 0; i < src.length(); i++) {
			final char c = src.charAt(i);

			if (c == '%') {
				if (i + 2 < src.length()) {
					final char c1 = src.charAt(i + 1);
					final char c2 = src.charAt(i + 2);

					if (c1 =='2') {
						if (c2 == 'D') {
							result.append('-');
							i += 2;
						} else if (c2 == 'E') {
							result.append('.');
							i += 2;
						} else {
							result.append(c, c1);
							i++;
						}

					} else if (c1 == '5') {
						if (c2 == 'F') {
							result.append('_');
							i += 2;
						} else {
							result.append(c, c1);
							i++;
						}

					} else if (c1 == '7') {
						if (c2 == 'E') {
							result.append('~');
							i += 2;
						} else {
							result.append(c, c1);
							i++;
						}

					} else {
						result.append(c); //TODO(AR) should this be % encoded
					}
				} else {
					result.append(c);
				}

			} else if (c == '*') {
				result.append('%', '2', 'A');

			} else if (c == '+') {
				result.append('%', '2', '0');

			} else {
				result.append(c);
			}
		}

		return new String(result.buf, 0, result.count);
	}
	
	public static String iriToURI(String uriPart) {
		String result = urlEncodeUtf8(uriPart);
		result = result.replaceAll("%23", "#");
		result = result.replaceAll("%2D", "-");
		result = result.replaceAll("%5F", "_");
		result = result.replaceAll("%2E", ".");
		result = result.replaceAll("%21", "!");
		result = result.replaceAll("%7E", "~");
		result = result.replaceAll("%2A", "*");
		result = result.replaceAll("%27", "'");
		result = result.replaceAll("%28", "(");
		result = result.replaceAll("%29", ")");
		result = result.replaceAll("%3B", ";");
		result = result.replaceAll("%2F", "/");
		result = result.replaceAll("%3F", "?");		
		result = result.replaceAll("%3A", ":");
		result = result.replaceAll("%40", "@");
		result = result.replaceAll("%26", "&");
		result = result.replaceAll("%3D", "=");		
		result = result.replaceAll("%2B", "+");
		result = result.replaceAll("%24", "\\$");
		result = result.replaceAll("%2C", ",");		
{'fraction_non_alphanumeric': 0.15155883943052803, 'fraction_numerical': 0.019553072625698324, 'mean_word_length': 2.9895758447160317, 'pattern_counts': {'":': 2, '<': 35, '<?xml version=': 0, '>': 24, 'https://': 2, 'lorem ipsum': 0, 'www.': 4, 'xml': 2}, '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': '7658303', 'n_tokens_mistral': 4393, 'n_tokens_neox': 3603, 'n_words': 1119}