QGIS API Documentation  3.6.0-Noosa (5873452)
qgspathresolver.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspathresolver.cpp
3  --------------------------------------
4  Date : February 2017
5  Copyright : (C) 2017 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgspathresolver.h"
17 
18 #include "qgis.h"
19 
20 #include <QFileInfo>
21 
22 
23 QgsPathResolver::QgsPathResolver( const QString &baseFileName )
24  : mBaseFileName( baseFileName )
25 {
26 }
27 
28 
29 QString QgsPathResolver::readPath( const QString &filename ) const
30 {
31  if ( filename.isEmpty() )
32  return QString();
33 
34  QString src = filename;
35 
36  if ( mBaseFileName.isNull() )
37  {
38  return src;
39  }
40 
41  // if this is a VSIFILE, remove the VSI prefix and append to final result
42  QString vsiPrefix = qgsVsiPrefix( src );
43  if ( ! vsiPrefix.isEmpty() )
44  {
45  // unfortunately qgsVsiPrefix returns prefix also for files like "/x/y/z.gz"
46  // so we need to check if we really have the prefix
47  if ( src.startsWith( QLatin1String( "/vsi" ), Qt::CaseInsensitive ) )
48  src.remove( 0, vsiPrefix.size() );
49  else
50  vsiPrefix.clear();
51  }
52 
53  // relative path should always start with ./ or ../
54  if ( !src.startsWith( QLatin1String( "./" ) ) && !src.startsWith( QLatin1String( "../" ) ) )
55  {
56 #if defined(Q_OS_WIN)
57  if ( src.startsWith( "\\\\" ) ||
58  src.startsWith( "//" ) ||
59  ( src[0].isLetter() && src[1] == ':' ) )
60  {
61  // UNC or absolute path
62  return vsiPrefix + src;
63  }
64 #else
65  if ( src[0] == '/' )
66  {
67  // absolute path
68  return vsiPrefix + src;
69  }
70 #endif
71 
72  // so this one isn't absolute, but also doesn't start // with ./ or ../.
73  // That means that it was saved with an earlier version of "relative path support",
74  // where the source file had to exist and only the project directory was stripped
75  // from the filename.
76 
77  QFileInfo pfi( mBaseFileName );
78  QString home = pfi.absolutePath();
79  if ( home.isEmpty() )
80  return vsiPrefix + src;
81 
82  QFileInfo fi( home + '/' + src );
83 
84  if ( !fi.exists() )
85  {
86  return vsiPrefix + src;
87  }
88  else
89  {
90  return vsiPrefix + fi.canonicalFilePath();
91  }
92  }
93 
94  QString srcPath = src;
95  QString projPath = mBaseFileName;
96 
97  if ( projPath.isEmpty() )
98  {
99  return vsiPrefix + src;
100  }
101 
102 #if defined(Q_OS_WIN)
103  srcPath.replace( '\\', '/' );
104  projPath.replace( '\\', '/' );
105 
106  bool uncPath = projPath.startsWith( "//" );
107 #endif
108 
109  QStringList srcElems = srcPath.split( '/', QString::SkipEmptyParts );
110  QStringList projElems = projPath.split( '/', QString::SkipEmptyParts );
111 
112 #if defined(Q_OS_WIN)
113  if ( uncPath )
114  {
115  projElems.insert( 0, "" );
116  projElems.insert( 0, "" );
117  }
118 #endif
119 
120  // remove project file element
121  projElems.removeLast();
122 
123  // append source path elements
124  projElems << srcElems;
125  projElems.removeAll( QStringLiteral( "." ) );
126 
127  // resolve ..
128  int pos;
129  while ( ( pos = projElems.indexOf( QStringLiteral( ".." ) ) ) > 0 )
130  {
131  // remove preceding element and ..
132  projElems.removeAt( pos - 1 );
133  projElems.removeAt( pos - 1 );
134  }
135 
136 #if !defined(Q_OS_WIN)
137  // make path absolute
138  projElems.prepend( QString() );
139 #endif
140 
141  return vsiPrefix + projElems.join( QStringLiteral( "/" ) );
142 }
143 
144 
145 QString QgsPathResolver::writePath( const QString &src ) const
146 {
147  if ( mBaseFileName.isEmpty() || src.isEmpty() )
148  {
149  return src;
150  }
151 
152  // Get projPath even if project has not been created yet
153  QFileInfo pfi( QFileInfo( mBaseFileName ).path() );
154  QString projPath = pfi.canonicalFilePath();
155 
156  // If project directory doesn't exit, fallback to absoluteFilePath : symbolic
157  // links won't be handled correctly, but that's OK as the path is "virtual".
158  if ( projPath.isEmpty() )
159  projPath = pfi.absoluteFilePath();
160 
161  if ( projPath.isEmpty() )
162  {
163  return src;
164  }
165 
166  // Strip "file://"
167  QFileInfo srcFileInfo( src.startsWith( QStringLiteral( "file://" ) ) ? src.mid( 7 ) : src );
168  QString srcPath = srcFileInfo.exists() ? srcFileInfo.canonicalFilePath() : src;
169 
170  // if this is a VSIFILE, remove the VSI prefix and append to final result
171  QString vsiPrefix = qgsVsiPrefix( src );
172  if ( ! vsiPrefix.isEmpty() )
173  {
174  srcPath.remove( 0, vsiPrefix.size() );
175  }
176 
177 #if defined( Q_OS_WIN )
178  const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
179 
180  srcPath.replace( '\\', '/' );
181 
182  if ( srcPath.startsWith( "//" ) )
183  {
184  // keep UNC prefix
185  srcPath = "\\\\" + srcPath.mid( 2 );
186  }
187 
188  projPath.replace( '\\', '/' );
189  if ( projPath.startsWith( "//" ) )
190  {
191  // keep UNC prefix
192  projPath = "\\\\" + projPath.mid( 2 );
193  }
194 #else
195  const Qt::CaseSensitivity cs = Qt::CaseSensitive;
196 #endif
197 
198  QStringList projElems = projPath.split( '/', QString::SkipEmptyParts );
199  QStringList srcElems = srcPath.split( '/', QString::SkipEmptyParts );
200 
201  projElems.removeAll( QStringLiteral( "." ) );
202  srcElems.removeAll( QStringLiteral( "." ) );
203 
204  // remove common part
205  int n = 0;
206  while ( !srcElems.isEmpty() &&
207  !projElems.isEmpty() &&
208  srcElems[0].compare( projElems[0], cs ) == 0 )
209  {
210  srcElems.removeFirst();
211  projElems.removeFirst();
212  n++;
213  }
214 
215  if ( n == 0 )
216  {
217  // no common parts; might not even be a file
218  return src;
219  }
220 
221  if ( !projElems.isEmpty() )
222  {
223  // go up to the common directory
224  for ( int i = 0; i < projElems.size(); i++ )
225  {
226  srcElems.insert( 0, QStringLiteral( ".." ) );
227  }
228  }
229  else
230  {
231  // let it start with . nevertheless,
232  // so relative path always start with either ./ or ../
233  srcElems.insert( 0, QStringLiteral( "." ) );
234  }
235 
236  return vsiPrefix + srcElems.join( QStringLiteral( "/" ) );
237 }
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
QString qgsVsiPrefix(const QString &path)
Definition: qgis.cpp:226
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
QgsPathResolver(const QString &baseFileName=QString())
Initialize path resolver with a base filename. Null filename means no conversion between relative/abs...