QGIS API Documentation  3.2.0-Bonn (bc43194)
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  QString src = filename;
32 
33  if ( mBaseFileName.isNull() )
34  {
35  return src;
36  }
37 
38  // if this is a VSIFILE, remove the VSI prefix and append to final result
39  QString vsiPrefix = qgsVsiPrefix( src );
40  if ( ! vsiPrefix.isEmpty() )
41  {
42  // unfortunately qgsVsiPrefix returns prefix also for files like "/x/y/z.gz"
43  // so we need to check if we really have the prefix
44  if ( src.startsWith( QLatin1String( "/vsi" ), Qt::CaseInsensitive ) )
45  src.remove( 0, vsiPrefix.size() );
46  else
47  vsiPrefix.clear();
48  }
49 
50  // relative path should always start with ./ or ../
51  if ( !src.startsWith( QLatin1String( "./" ) ) && !src.startsWith( QLatin1String( "../" ) ) )
52  {
53 #if defined(Q_OS_WIN)
54  if ( src.startsWith( "\\\\" ) ||
55  src.startsWith( "//" ) ||
56  ( src[0].isLetter() && src[1] == ':' ) )
57  {
58  // UNC or absolute path
59  return vsiPrefix + src;
60  }
61 #else
62  if ( src[0] == '/' )
63  {
64  // absolute path
65  return vsiPrefix + src;
66  }
67 #endif
68 
69  // so this one isn't absolute, but also doesn't start // with ./ or ../.
70  // That means that it was saved with an earlier version of "relative path support",
71  // where the source file had to exist and only the project directory was stripped
72  // from the filename.
73 
74  QFileInfo pfi( mBaseFileName );
75  QString home = pfi.absolutePath();
76  if ( home.isEmpty() )
77  return vsiPrefix + src;
78 
79  QFileInfo fi( home + '/' + src );
80 
81  if ( !fi.exists() )
82  {
83  return vsiPrefix + src;
84  }
85  else
86  {
87  return vsiPrefix + fi.canonicalFilePath();
88  }
89  }
90 
91  QString srcPath = src;
92  QString projPath = mBaseFileName;
93 
94  if ( projPath.isEmpty() )
95  {
96  return vsiPrefix + src;
97  }
98 
99 #if defined(Q_OS_WIN)
100  srcPath.replace( '\\', '/' );
101  projPath.replace( '\\', '/' );
102 
103  bool uncPath = projPath.startsWith( "//" );
104 #endif
105 
106  QStringList srcElems = srcPath.split( '/', QString::SkipEmptyParts );
107  QStringList projElems = projPath.split( '/', QString::SkipEmptyParts );
108 
109 #if defined(Q_OS_WIN)
110  if ( uncPath )
111  {
112  projElems.insert( 0, "" );
113  projElems.insert( 0, "" );
114  }
115 #endif
116 
117  // remove project file element
118  projElems.removeLast();
119 
120  // append source path elements
121  projElems << srcElems;
122  projElems.removeAll( QStringLiteral( "." ) );
123 
124  // resolve ..
125  int pos;
126  while ( ( pos = projElems.indexOf( QStringLiteral( ".." ) ) ) > 0 )
127  {
128  // remove preceding element and ..
129  projElems.removeAt( pos - 1 );
130  projElems.removeAt( pos - 1 );
131  }
132 
133 #if !defined(Q_OS_WIN)
134  // make path absolute
135  projElems.prepend( QLatin1String( "" ) );
136 #endif
137 
138  return vsiPrefix + projElems.join( QStringLiteral( "/" ) );
139 }
140 
141 
142 QString QgsPathResolver::writePath( const QString &src ) const
143 {
144  if ( mBaseFileName.isEmpty() || src.isEmpty() )
145  {
146  return src;
147  }
148 
149  // Get projPath even if project has not been created yet
150  QFileInfo pfi( QFileInfo( mBaseFileName ).path() );
151  QString projPath = pfi.canonicalFilePath();
152 
153  // If project directory doesn't exit, fallback to absoluteFilePath : symbolic
154  // links won't be handled correctly, but that's OK as the path is "virtual".
155  if ( projPath.isEmpty() )
156  projPath = pfi.absoluteFilePath();
157 
158  if ( projPath.isEmpty() )
159  {
160  return src;
161  }
162 
163  QFileInfo srcFileInfo( src );
164  QString srcPath = srcFileInfo.exists() ? srcFileInfo.canonicalFilePath() : src;
165 
166  // if this is a VSIFILE, remove the VSI prefix and append to final result
167  QString vsiPrefix = qgsVsiPrefix( src );
168  if ( ! vsiPrefix.isEmpty() )
169  {
170  srcPath.remove( 0, vsiPrefix.size() );
171  }
172 
173 #if defined( Q_OS_WIN )
174  const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
175 
176  srcPath.replace( '\\', '/' );
177 
178  if ( srcPath.startsWith( "//" ) )
179  {
180  // keep UNC prefix
181  srcPath = "\\\\" + srcPath.mid( 2 );
182  }
183 
184  projPath.replace( '\\', '/' );
185  if ( projPath.startsWith( "//" ) )
186  {
187  // keep UNC prefix
188  projPath = "\\\\" + projPath.mid( 2 );
189  }
190 #else
191  const Qt::CaseSensitivity cs = Qt::CaseSensitive;
192 #endif
193 
194  QStringList projElems = projPath.split( '/', QString::SkipEmptyParts );
195  QStringList srcElems = srcPath.split( '/', QString::SkipEmptyParts );
196 
197  projElems.removeAll( QStringLiteral( "." ) );
198  srcElems.removeAll( QStringLiteral( "." ) );
199 
200  // remove common part
201  int n = 0;
202  while ( !srcElems.isEmpty() &&
203  !projElems.isEmpty() &&
204  srcElems[0].compare( projElems[0], cs ) == 0 )
205  {
206  srcElems.removeFirst();
207  projElems.removeFirst();
208  n++;
209  }
210 
211  if ( n == 0 )
212  {
213  // no common parts; might not even be a file
214  return src;
215  }
216 
217  if ( !projElems.isEmpty() )
218  {
219  // go up to the common directory
220  for ( int i = 0; i < projElems.size(); i++ )
221  {
222  srcElems.insert( 0, QStringLiteral( ".." ) );
223  }
224  }
225  else
226  {
227  // let it start with . nevertheless,
228  // so relative path always start with either ./ or ../
229  srcElems.insert( 0, QStringLiteral( "." ) );
230  }
231 
232  return vsiPrefix + srcElems.join( QStringLiteral( "/" ) );
233 }
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:219
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...