Created
April 1, 2015 19:56
-
-
Save koo5/9b216ddf5a58a91c83a3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// package com.github.jsonldjava.utils; | |
// import java.net.URI; | |
// import java.net.URISyntaxException; | |
// import java.util.ArrayList; | |
// import java.util.Arrays; | |
// import java.util.List; | |
// import java.util.regex.Matcher; | |
// import java.util.regex.Pattern; | |
#include <boost/logic/tribool.hpp> | |
BOOST_TRIBOOL_THIRD_STATE ( bnull ) | |
#include <regex> | |
#include <string> | |
//using namespace std; | |
typedef std::string String; | |
typedef bool boolean; | |
typedef boost::tribool Boolean; | |
typedef std::basic_regex<String> Pattern; | |
std::regex parser(("^(?:([^:\\/?#]+):)?(?:\\/\\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\\/?#]*)(?::(\\d*))?))?((((?:[^?#\\/]*\\/)*)([^?#]*))(?:\\?([^#]*))?(?:#(.*))?)")); | |
class JsonLdUrl { | |
public: String href = ""; | |
public: String protocol = ""; | |
public: String host = ""; | |
public: String auth = ""; | |
public: String user = ""; | |
public: String password = ""; | |
public: String hostname = ""; | |
public: String port = ""; | |
public: String relative = ""; | |
public: String path = ""; | |
public: String directory = ""; | |
public: String file = ""; | |
public: String query = ""; | |
public: String hash = ""; | |
// things not populated by the regex (NOTE: i don't think it matters if | |
// these are null or "" to start with) | |
public: String pathname = null; | |
public: String normalizedPath = null; | |
public: String authority = null; | |
//private: static Pattern parser = Pattern ( "^(?:([^:\\/?#]+):)?(?:\\/\\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\\/?#]*)(?::(\\d*))?))?((((?:[^?#\\/]*\\/)*)([^?#]*))(?:\\?([^#]*))?(?:#(.*))?)" ); | |
public: static JsonLdUrl parse ( String url ) { | |
const JsonLdUrl rval = new JsonLdUrl(); | |
rval.href = url; | |
const Matcher matcher = parser.matcher ( url ); | |
if ( matcher.matches() ) { | |
if ( matcher.group ( 1 ) != null ) | |
rval.protocol = matcher.group ( 1 ); | |
if ( matcher.group ( 2 ) != null ) | |
rval.host = matcher.group ( 2 ); | |
if ( matcher.group ( 3 ) != null ) | |
rval.auth = matcher.group ( 3 ); | |
if ( matcher.group ( 4 ) != null ) | |
rval.user = matcher.group ( 4 ); | |
if ( matcher.group ( 5 ) != null ) | |
rval.password = matcher.group ( 5 ); | |
if ( matcher.group ( 6 ) != null ) | |
rval.hostname = matcher.group ( 6 ); | |
if ( matcher.group ( 7 ) != null ) | |
rval.port = matcher.group ( 7 ); | |
if ( matcher.group ( 8 ) != null ) | |
rval.relative = matcher.group ( 8 ); | |
if ( matcher.group ( 9 ) != null ) | |
rval.path = matcher.group ( 9 ); | |
if ( matcher.group ( 10 ) != null ) | |
rval.directory = matcher.group ( 10 ); | |
if ( matcher.group ( 11 ) != null ) | |
rval.file = matcher.group ( 11 ); | |
if ( matcher.group ( 12 ) != null ) | |
rval.query = matcher.group ( 12 ); | |
if ( matcher.group ( 13 ) != null ) | |
rval.hash = matcher.group ( 13 ); | |
// normalize to node.js API | |
if ( !"".equals ( rval.host ) && "".equals ( rval.path ) ) | |
rval.path = "/"; | |
rval.pathname = rval.path; | |
parseAuthority ( rval ); | |
rval.normalizedPath = removeDotSegments ( rval.pathname, !"".equals ( rval.authority ) ); | |
if ( !"".equals ( rval.query ) ) | |
rval.path += "?" + rval.query; | |
if ( !"".equals ( rval.protocol ) ) | |
rval.protocol += ":"; | |
if ( !"".equals ( rval.hash ) ) | |
rval.hash = "#" + rval.hash; | |
return rval; | |
} | |
return rval; | |
} | |
/** | |
Removes dot segments from a JsonLdUrl path. | |
@param path | |
the path to remove dot segments from. | |
@param hasAuthority | |
true if the JsonLdUrl has an authority, false if not. | |
@return The URL without the dot segments | |
*/ | |
public: static String removeDotSegments ( String path, boolean hasAuthority ) { | |
String rval = ""; | |
if ( path.indexOf ( "/" ) == 0 ) | |
rval = "/"; | |
// RFC 3986 5.2.4 (reworked) | |
const List<String> input = new ArrayList<String> ( Arrays.asList ( path.split ( "/" ) ) ); | |
if ( path.endsWith ( "/" ) ) { | |
// javascript .split includes a blank entry if the string ends with | |
// the delimiter, java .split does not so we need to add it manually | |
input.add ( "" ); | |
} | |
const List<String> output = new ArrayList<String>(); | |
for ( int i = 0; i < input.size(); i++ ) { | |
if ( ".".equals ( input.get ( i ) ) || ( "".equals ( input.get ( i ) ) && input.size() - i > 1 ) ) { | |
// input.remove(0); | |
continue; | |
} | |
if ( "..".equals ( input.get ( i ) ) ) { | |
// input.remove(0); | |
if ( hasAuthority | |
|| ( output.size() > 0 && !"..".equals ( output.get ( output.size() - 1 ) ) ) ) { | |
// [].pop() doesn't fail, to replicate this we need to check | |
// that there is something to remove | |
if ( output.size() > 0 ) | |
output.remove ( output.size() - 1 ); | |
} else | |
output.add ( ".." ); | |
continue; | |
} | |
output.add ( input.get ( i ) ); | |
// input.remove(0); | |
} | |
if ( output.size() > 0 ) { | |
rval += output.get ( 0 ); | |
for ( int i = 1; i < output.size(); i++ ) | |
rval += "/" + output.get ( i ); | |
} | |
return rval; | |
} | |
public: static String removeBase ( boost::variant<String, null, JsonLdUrl> baseobj, String iri ) { | |
JsonLdUrl base; | |
if ( String* base_as_string = boost::get<String>(baseobj) ) | |
base = JsonLdUrl.parse ( base_as_string ); | |
elif (base = boost::get<JsonLdUrl>(baseobj)) | |
{;} | |
else | |
return iri; | |
// establish base root | |
String root = ""; | |
if ( !"".equals ( base.href ) ) | |
root += ( base.protocol ) + "//" + base.authority; | |
// support network-path reference with empty base | |
else if ( iri.indexOf ( "//" ) != 0 ) | |
root += "//"; | |
// IRI not relative to base | |
if ( iri.indexOf ( root ) != 0 ) | |
return iri; | |
// remove root from IRI and parse remainder | |
const JsonLdUrl rel = JsonLdUrl.parse ( iri.substring ( root.length() ) ); | |
// remove path segments that match | |
const List<String> baseSegments = new ArrayList<String> ( Arrays.asList ( base.normalizedPath | |
.split ( "/" ) ) ); | |
if ( base.normalizedPath.endsWith ( "/" ) ) | |
baseSegments.add ( "" ); | |
const List<String> iriSegments = new ArrayList<String> ( Arrays.asList ( rel.normalizedPath | |
.split ( "/" ) ) ); | |
if ( rel.normalizedPath.endsWith ( "/" ) ) | |
iriSegments.add ( "" ); | |
while ( baseSegments.size() > 0 && iriSegments.size() > 0 ) { | |
if ( !baseSegments.get ( 0 ).equals ( iriSegments.get ( 0 ) ) ) | |
break; | |
if ( baseSegments.size() > 0 ) | |
baseSegments.remove ( 0 ); | |
if ( iriSegments.size() > 0 ) | |
iriSegments.remove ( 0 ); | |
} | |
// use '../' for each non-matching base segment | |
String rval = ""; | |
if ( baseSegments.size() > 0 ) { | |
// don't count the last segment if it isn't a path (doesn't end in | |
// '/') | |
// don't count empty first segment, it means base began with '/' | |
if ( !base.normalizedPath.endsWith ( "/" ) || "".equals ( baseSegments.get ( 0 ) ) ) | |
baseSegments.remove ( baseSegments.size() - 1 ); | |
for ( int i = 0; i < baseSegments.size(); ++i ) | |
rval += "../"; | |
} | |
// prepend remaining segments | |
if ( iriSegments.size() > 0 ) | |
rval += iriSegments.get ( 0 ); | |
for ( int i = 1; i < iriSegments.size(); i++ ) | |
rval += "/" + iriSegments.get ( i ); | |
// add query and hash | |
if ( !"".equals ( rel.query ) ) | |
rval += "?" + rel.query; | |
if ( !"".equals ( rel.hash ) ) | |
rval += rel.hash; | |
if ( "".equals ( rval ) ) | |
rval = "./"; | |
return rval; | |
} | |
public: static String resolve ( String baseUri, String pathToResolve ) { | |
// TODO: some input will need to be normalized to perform the expected | |
// result with java | |
// TODO: we can do this without using java URI! | |
if ( baseUri == null ) | |
return pathToResolve; | |
if ( pathToResolve == null || "".equals ( pathToResolve.trim() ) ) | |
return baseUri; | |
try { | |
URI uri = new URI ( baseUri ); | |
// query string parsing | |
if ( pathToResolve.startsWith ( "?" ) ) { | |
// drop fragment from uri if it has one | |
if ( uri.getFragment() != null ) | |
uri = new URI ( uri.getScheme(), uri.getAuthority(), uri.getPath(), null, null ); | |
// add query to the end manually (as URI.resolve does it wrong) | |
return uri.toString() + pathToResolve; | |
} | |
uri = uri.resolve ( pathToResolve ); | |
// java doesn't discard unnecessary dot segments | |
String path = uri.getPath(); | |
if ( path != null ) | |
path = JsonLdUrl.removeDotSegments ( uri.getPath(), true ); | |
return new URI ( uri.getScheme(), uri.getAuthority(), path, uri.getQuery(), | |
uri.getFragment() ).toString(); | |
} catch ( const URISyntaxException e ) { | |
return null; | |
} | |
} | |
/** | |
Parses the authority for the pre-parsed given JsonLdUrl. | |
@param parsed | |
the pre-parsed JsonLdUrl. | |
*/ | |
private: static void parseAuthority ( JsonLdUrl parsed ) { | |
// parse authority for unparsed relative network-path reference | |
if ( parsed.href.indexOf ( ":" ) == -1 && parsed.href.indexOf ( "//" ) == 0 | |
&& "".equals ( parsed.host ) ) { | |
// must parse authority from pathname | |
parsed.pathname = parsed.pathname.substring ( 2 ); | |
const int idx = parsed.pathname.indexOf ( "/" ); | |
if ( idx == -1 ) { | |
parsed.authority = parsed.pathname; | |
parsed.pathname = ""; | |
} else { | |
parsed.authority = parsed.pathname.substring ( 0, idx ); | |
parsed.pathname = parsed.pathname.substring ( idx ); | |
} | |
} else { | |
// construct authority | |
parsed.authority = parsed.host; | |
if ( !"".equals ( parsed.auth ) ) | |
parsed.authority = parsed.auth + "@" + parsed.authority; | |
} | |
} | |
} | |
; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment