1 /**
2  * @file    SBMLUri.cpp
3  * @brief   Implementation of SBMLUri, the utility class for handling uris.
4  * @author  Frank Bergmann
5  *
6  * <!--------------------------------------------------------------------------
7  * This file is part of libSBML.  Please visit http://sbml.org for more
8  * information about SBML, and the latest version of libSBML.
9  *
10  * Copyright (C) 2020 jointly by the following organizations:
11  *     1. California Institute of Technology, Pasadena, CA, USA
12  *     2. University of Heidelberg, Heidelberg, Germany
13  *     3. University College London, London, UK
14  *
15  * Copyright (C) 2019 jointly by the following organizations:
16  *     1. California Institute of Technology, Pasadena, CA, USA
17  *     2. University of Heidelberg, Heidelberg, Germany
18  *
19  * Copyright (C) 2013-2018 jointly by the following organizations:
20  *     1. California Institute of Technology, Pasadena, CA, USA
21  *     2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
22  *     3. University of Heidelberg, Heidelberg, Germany
23  *
24  * Copyright 2011-2012 jointly by the following organizations:
25  *     1. California Institute of Technology, Pasadena, CA, USA
26  *     2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
27  *
28  * This library is free software; you can redistribute it and/or modify it
29  * under the terms of the GNU Lesser General Public License as published by
30  * the Free Software Foundation.  A copy of the license agreement is provided
31  * in the file named "LICENSE.txt" included with this software distribution
32  * and also available online as http://sbml.org/software/libsbml/license.html
33  * ------------------------------------------------------------------------ -->
34  */
35 
36 #include <sbml/packages/comp/util/SBMLUri.h>
37 #include <sbml/SBMLDocument.h>
38 #include <sbml/SBMLConstructorException.h>
39 
40 #ifdef __cplusplus
41 
42 #include <cctype>
43 #include <functional>
44 #include <iterator>
45 #include <string>
46 #include <algorithm>
47 
48 using namespace std;
49 LIBSBML_CPP_NAMESPACE_BEGIN
50 
SBMLUri(const std::string & uri)51 SBMLUri::SBMLUri (const std::string& uri)
52 {
53   parse(uri);
54 }
55 
56 
57 /*
58  * Copy constructor.
59  */
SBMLUri(const SBMLUri & orig)60 SBMLUri::SBMLUri(const SBMLUri& orig)
61  : mScheme (orig.mScheme)
62  , mHost   (orig.mHost)
63  , mPath   (orig.mPath)
64  , mQuery  (orig.mQuery)
65  , mUri    (orig.mUri)
66 {
67 }
68 
69 
70 /*
71  * Destroy this object.
72  */
~SBMLUri()73 SBMLUri::~SBMLUri ()
74 {
75 
76 }
77 
78 
79 /*
80  * Assignment operator for SBMLUri.
81  */
82 SBMLUri&
operator =(const SBMLUri & rhs)83 SBMLUri::operator=(const SBMLUri& rhs)
84 {
85   if(&rhs!=this)
86   {
87     mScheme = rhs.mScheme;
88     mHost = rhs.mHost;
89     mPath = rhs.mPath;
90     mQuery = rhs.mQuery;
91     mUri = rhs.mUri;
92   }
93 
94   return *this;
95 }
96 
97 /*
98  * Assignment operator for strings.
99  */
100 SBMLUri&
operator =(const std::string & uri)101 SBMLUri::operator=(const std::string& uri)
102 {
103   parse(uri);
104   return *this;
105 }
106 
107 
108 SBMLUri*
clone() const109 SBMLUri::clone () const
110 {
111   return new SBMLUri(*this);
112 }
113 
114 struct replace_back_slash
115 {
operator ()replace_back_slash116 void operator()(char& c) { if(c == '\\') c = '/'; }
117 };
118 
119 /*
120  * adapted from: http://stackoverflow.com/questions/2616011/easy-way-to-parse-a-url-in-c-cross-platform
121  */
122 void
parse(const std::string & uri)123 SBMLUri::parse(const std::string& uri)
124 {
125   mScheme = "";
126   mHost = "";
127   mQuery = "";
128   mPath = "";
129   mUri = uri;
130 
131   std::for_each( mUri.begin(), mUri.end(), replace_back_slash() );
132 
133   const std::string constUri(mUri);
134 
135   string prot_end("://");
136   string::const_iterator prot_i = search(constUri.begin(), constUri.end(),
137     prot_end.begin(), prot_end.end());
138   if (prot_i == constUri.end())
139   {
140     // not found ...
141     prot_end = ":";
142     prot_i = search(constUri.begin(), constUri.end(),
143     prot_end.begin(), prot_end.end());
144     if (prot_i == constUri.end() || prot_i == constUri.begin() + 1)
145     {
146       // if we still have not found it assume this is a file uri
147       mScheme = "file";
148       mPath = constUri ;
149       mUri = mScheme + ":///" + mPath;
150       return;
151     }
152   }
153 
154   mScheme.reserve((size_t)distance(constUri.begin(), prot_i));
155 #ifdef __BORLANDC__
156   transform(constUri.begin(), prot_i,
157     back_inserter(mScheme),
158     (int(*)(int))(tolower)); // scheme is icase
159 #else
160   transform(constUri.begin(), prot_i,
161     back_inserter(mScheme),
162     ptr_fun<int,int>(tolower)); // scheme is icase
163 #endif
164 
165   if( prot_i == constUri.end() )
166   {
167     return;
168   }
169 
170 #ifdef __BORLANDC__
171   advance(prot_i, prot_end.length());
172 #else
173   advance(prot_i, (string::const_iterator::difference_type) prot_end.length());
174 #endif
175   if ((prot_i + 1) != constUri.end() && *(prot_i + 1) == ':')
176   {
177     // turns out there are invalid urls being used internally, of the form
178     // file:drive:/ ... this is just plain wrong but needs to be parsed correctly
179     //
180     mPath.reserve((size_t)distance(prot_i, constUri.end()));
181 #ifdef __BORLANDC__
182     mPath = std::string(prot_i, constUri.end());
183 #else
184     mPath.assign(prot_i, constUri.end());
185 #endif
186     // but we ought to fix the URI!
187     mUri = mScheme + ":///" + mPath;
188     return;
189   }
190 
191   string::const_iterator path_i = find(prot_i, constUri.end(), '/');
192   if (mScheme != "file" && mScheme != "urn")
193   {
194     // file won't have a host (or could assume localhost)
195     mHost.reserve((size_t)distance(prot_i, path_i));
196 #ifdef __BORLANDC__
197     transform(prot_i, path_i,
198      back_inserter(mHost),
199      (int(*)(int))(tolower)); // host is icase
200 #else
201     transform(prot_i, path_i,
202      back_inserter(mHost),
203      ptr_fun<int,int>(tolower)); // host is icase
204 #endif
205   }
206   else if (mScheme == "urn")
207   {
208     // special handling for the miriam scheme, as it is something we ought
209     // to support
210     size_t pos = constUri.rfind(':');
211     mScheme = constUri.substr(0,pos);
212     mPath = constUri.substr(pos+1, constUri.length() - pos +1);
213     return;
214   }
215   else
216   {
217 #ifdef __BORLANDC__
218     mPath = std::string(prot_i, path_i);
219 #else
220     mPath.assign(prot_i, path_i);
221 #endif
222     if (mPath.size() > 0 && mPath[0] == '/')
223     {
224 #ifdef __BORLANDC__
225       mPath = std::string(mPath.begin() +1, mPath.end());
226 #else
227       mPath.assign(mPath.begin() +1, mPath.end());
228 #endif
229     }
230   }
231 
232   if (path_i == constUri.end())
233   {
234     return;
235   }
236 
237   string::const_iterator query_i = find(path_i, constUri.end(), '?');
238 #ifdef __BORLANDC__
239   mPath = std::string(path_i, query_i);
240 #else
241   mPath.assign(path_i, query_i);
242 #endif
243 
244   if (mPath.size() > 0 && mPath[0] == '/')
245   {
246 #ifdef __BORLANDC__
247 	 mPath = std::string(mPath.begin() +1, mPath.end());
248 #else
249       mPath.assign(mPath.begin() +1, mPath.end());
250 #endif
251   }
252 
253   if( query_i != constUri.end() )
254     ++query_i;
255 
256 #ifdef __BORLANDC__
257   mQuery = std::string(query_i, constUri.end());
258 #else
259   mQuery.assign(query_i, constUri.end());
260 #endif
261 }
262 
263 SBMLUri
relativeTo(const std::string & uri) const264 SBMLUri::relativeTo(const std::string& uri) const
265 {
266   SBMLUri other(uri);
267   other.mScheme = mScheme;
268   other.mHost = mHost;
269   bool slashNeeded = ((!other.mPath.empty() && other.mPath[0] != '/') ||
270 		(!mPath.empty() && !other.mPath.empty() && other.mPath[0] != '/' && mPath[mPath.length() -1 ] != '/') ||
271 		(!mPath.empty() && other.mPath.empty() && mPath[mPath.length() -1 ] != '/') );
272 
273   if (slashNeeded && other.mPath.length() > 2 && other.mPath[1] == ':')
274   {
275 	// the uri is a full path with drive letter
276 	return other;
277   }
278 
279   other.mPath = mPath + (slashNeeded  ? "/" : "") + other.mPath;
280   other.mUri = mScheme + "://" + mHost + (slashNeeded  ? "/" : "") + other.mPath;
281   if (!other.mQuery.empty())
282     other.mUri += "?" + other.mQuery;
283 
284   return other;
285 }
286 
287 const std::string&
getScheme() const288 SBMLUri::getScheme() const
289 {
290   return mScheme;
291 }
292 
293 const std::string&
getHost() const294 SBMLUri::getHost() const
295 {
296   return mHost;
297 }
298 
299 const std::string&
getPath() const300 SBMLUri::getPath() const
301 {
302   return mPath;
303 }
304 
305 const std::string&
getUri() const306 SBMLUri::getUri() const
307 {
308   return mUri;
309 }
310 
311 const std::string&
getQuery() const312 SBMLUri::getQuery() const
313 {
314   return mQuery;
315 }
316 
317 /** @cond doxygenIgnored */
318 /** @endcond */
319 
320 LIBSBML_CPP_NAMESPACE_END
321 
322 #endif  /* __cplusplus */
323 
324 
325