/net.sf.eclipsefp.haskell.ui/src/net/sf/eclipsefp/haskell/ui/internal/refactoring/participants/Util.java

https://github.com/thiagoarrais/eclipsefp · Java · 469 lines · 393 code · 48 blank · 28 comment · 63 complexity · 2eafb90865be85212d959f89dc57b5a0 MD5 · raw file

  1. /**
  2. * (c) 2011, Alejandro Serrano
  3. * Released under the terms of the EPL.
  4. */
  5. package net.sf.eclipsefp.haskell.ui.internal.refactoring.participants;
  6. import java.util.ArrayList;
  7. import java.util.HashSet;
  8. import java.util.List;
  9. import java.util.Set;
  10. import net.sf.eclipsefp.haskell.core.cabalmodel.CabalSyntax;
  11. import net.sf.eclipsefp.haskell.core.cabalmodel.PackageDescription;
  12. import net.sf.eclipsefp.haskell.core.cabalmodel.PackageDescriptionLoader;
  13. import net.sf.eclipsefp.haskell.core.cabalmodel.PackageDescriptionStanza;
  14. import net.sf.eclipsefp.haskell.core.cabalmodel.RealValuePosition;
  15. import net.sf.eclipsefp.haskell.scion.client.ScionInstance;
  16. import net.sf.eclipsefp.haskell.util.FileUtil;
  17. import org.eclipse.core.resources.IFile;
  18. import org.eclipse.core.resources.IProject;
  19. import org.eclipse.core.resources.IResource;
  20. import org.eclipse.core.resources.IResourceVisitor;
  21. import org.eclipse.core.runtime.CoreException;
  22. import org.eclipse.core.runtime.IPath;
  23. import org.eclipse.core.runtime.Path;
  24. import org.eclipse.jface.text.BadLocationException;
  25. import org.eclipse.jface.text.IDocument;
  26. import org.eclipse.jface.text.IRegion;
  27. import org.eclipse.ui.editors.text.TextFileDocumentProvider;
  28. /**
  29. * Utilities and shared changes creation for participants.
  30. * @author Alejandro Serrano
  31. *
  32. */
  33. public class Util {
  34. public static Set<IPath> getPaths( final IProject project ) {
  35. HashSet<IPath> paths = new HashSet<IPath>();
  36. try {
  37. IFile cabalF = ScionInstance.getCabalFile( project );
  38. PackageDescription pd = PackageDescriptionLoader.load( cabalF );
  39. List<PackageDescriptionStanza> lpds = pd.getStanzas();
  40. for( PackageDescriptionStanza pds: lpds ) {
  41. String propList = pds.getProperties().get(
  42. CabalSyntax.FIELD_HS_SOURCE_DIRS.getCabalName().toLowerCase() );
  43. propList = propList == null ? "" : propList;
  44. List<String> props = PackageDescriptionLoader.parseList( propList );
  45. for (String prop : props) {
  46. IPath prefixPath = Path.fromPortableString( prop );
  47. paths.add( prefixPath );
  48. }
  49. }
  50. } catch (Exception e) {
  51. return new HashSet<IPath>();
  52. }
  53. return paths;
  54. }
  55. public static Set<IPath> getStanzaPaths( final IProject project,
  56. final IFile file ) {
  57. HashSet<IPath> paths = new HashSet<IPath>();
  58. try {
  59. IFile cabalF = ScionInstance.getCabalFile( project );
  60. PackageDescription pd = PackageDescriptionLoader.load( cabalF );
  61. List<PackageDescriptionStanza> lpds = pd.getStanzas();
  62. for( PackageDescriptionStanza pds: lpds ) {
  63. String propList = pds.getProperties().get(
  64. CabalSyntax.FIELD_HS_SOURCE_DIRS.getCabalName().toLowerCase() );
  65. propList = propList == null ? "" : propList;
  66. List<String> props = PackageDescriptionLoader.parseList( propList );
  67. // If the file is in one of these directories, we should change it
  68. boolean shouldBeAdded = false;
  69. for (String prop : props) {
  70. IPath prefixPath = Path.fromPortableString( prop );
  71. if (prefixPath.isPrefixOf( file.getProjectRelativePath() )) {
  72. shouldBeAdded = true;
  73. }
  74. }
  75. // If should be changed, add every directory in the stanza
  76. if (shouldBeAdded) {
  77. for (String prop : props) {
  78. IPath prefixPath = Path.fromPortableString( prop );
  79. paths.add( prefixPath );
  80. }
  81. }
  82. }
  83. } catch (Exception e) {
  84. return new HashSet<IPath>();
  85. }
  86. return paths;
  87. }
  88. public static String getModuleName( final IResource resource ) {
  89. return getModuleName( resource.getProject(),
  90. resource.getProjectRelativePath() );
  91. }
  92. public static List<IResource> getHaskellFiles( final IProject project,
  93. final IFile file ) {
  94. final ArrayList<IResource> resources = new ArrayList<IResource>();
  95. final Set<IPath> allowedPaths = getStanzaPaths( project, file );
  96. try {
  97. project.accept( new IResourceVisitor() {
  98. public boolean visit( final IResource resource ) {
  99. if( resource instanceof IFile && !( file.equals( resource ) )
  100. && FileUtil.hasHaskellExtension( resource )) {
  101. // Check if we are in one of the allowed paths
  102. for (IPath allowedPath : allowedPaths) {
  103. if (allowedPath.isPrefixOf( resource.getProjectRelativePath() )) {
  104. resources.add( resource );
  105. break;
  106. }
  107. }
  108. }
  109. return true;
  110. }
  111. } );
  112. } catch( Exception e ) {
  113. // Do nothing
  114. }
  115. return resources;
  116. }
  117. public static String getModuleName( final IProject project,
  118. final IPath projectRelativePath ) {
  119. String path = projectRelativePath.toPortableString();
  120. for( IPath prefix: getPaths( project ) ) {
  121. String prefixPath = prefix.toPortableString();
  122. if( path.startsWith( prefixPath ) ) {
  123. String filePath = path.substring( prefixPath.length() + 1 );
  124. if( filePath.endsWith( ".hs" ) ) {
  125. return filePath.substring( 0, filePath.length() - 3 ).replace( '/',
  126. '.' );
  127. } else if( filePath.endsWith( ".lhs" ) ) {
  128. return filePath.substring( 0, filePath.length() - 4 ).replace( '/',
  129. '.' );
  130. }
  131. }
  132. }
  133. return null;
  134. }
  135. public static int getModuleNameOffset( final IResource resource ) {
  136. TextFileDocumentProvider provider = new TextFileDocumentProvider();
  137. try {
  138. provider.connect( resource );
  139. } catch( CoreException e ) {
  140. return -1;
  141. }
  142. int offset = -1;
  143. try {
  144. IDocument doc = provider.getDocument( resource );
  145. int numberOfLines = doc.getNumberOfLines();
  146. for( int i = 0; i < numberOfLines; i++ ) {
  147. IRegion region = doc.getLineInformation( i );
  148. String text = doc.get( region.getOffset(), region.getLength() );
  149. String[] words = text.split( " " );
  150. if( words[ 0 ].equals( "module" ) ) {
  151. // We are in a module declaration
  152. String moduleName = words[ 1 ];
  153. if( moduleName.length() == 0 ) {
  154. moduleName = words[ 2 ];
  155. }
  156. int moduleNamePos = text.indexOf( moduleName );
  157. offset = region.getOffset() + moduleNamePos;
  158. break;
  159. }
  160. }
  161. } catch( BadLocationException e ) {
  162. // This should not happen
  163. } finally {
  164. provider.disconnect( resource );
  165. }
  166. return offset;
  167. }
  168. public static List<Integer> getImportModuleOffsets( final IResource resource,
  169. final String moduleName ) {
  170. TextFileDocumentProvider provider = new TextFileDocumentProvider();
  171. ArrayList<Integer> offsets = new ArrayList<Integer>();
  172. try {
  173. provider.connect( resource );
  174. } catch( CoreException e ) {
  175. return offsets;
  176. }
  177. try {
  178. IDocument doc = provider.getDocument( resource );
  179. int numberOfLines = doc.getNumberOfLines();
  180. for( int i = 0; i < numberOfLines; i++ ) {
  181. IRegion region = doc.getLineInformation( i );
  182. String text = doc.get( region.getOffset(), region.getLength() );
  183. String[] words = text.split( " " );
  184. for( int j = 0; j < words.length; j++ ) {
  185. if( words[ j ].equals( "module" ) ) {
  186. try {
  187. String mname = words[ j + 1 ];
  188. if( mname.length() == 0 ) {
  189. mname = words[ j + 2 ];
  190. }
  191. if( mname.equals( moduleName ) ) {
  192. int modulePos = text.indexOf( mname );
  193. offsets.add( region.getOffset() + modulePos );
  194. }
  195. } catch( Exception e ) {
  196. // We are out of bounds
  197. }
  198. break;
  199. } else if( words[ j ].equals( "import" ) ) {
  200. try {
  201. String mname = words[ j + 1 ];
  202. if( mname.length() == 0 ) {
  203. mname = words[ j + 2 ];
  204. }
  205. if( mname.equals( "qualified" ) ) {
  206. mname = words[ j + 3 ];
  207. if( mname.length() == 0 ) {
  208. mname = words[ j + 4 ];
  209. }
  210. }
  211. if( mname.equals( moduleName ) ) {
  212. int modulePos = text.indexOf( mname );
  213. offsets.add( region.getOffset() + modulePos );
  214. }
  215. } catch( Exception e ) {
  216. // We are out of bounds
  217. }
  218. break;
  219. }
  220. }
  221. }
  222. } catch( BadLocationException e ) {
  223. // This should not happen
  224. } finally {
  225. provider.disconnect( resource );
  226. }
  227. return offsets;
  228. }
  229. public static String newCabalFile( final IProject project,
  230. final IFile oldFile, final String newModuleName ) {
  231. TextFileDocumentProvider provider = new TextFileDocumentProvider();
  232. String oldModuleName = getModuleName( oldFile );
  233. IFile cabalF = ScionInstance.getCabalFile( project );
  234. PackageDescription pd = null;
  235. try {
  236. pd = PackageDescriptionLoader.load( cabalF );
  237. provider.connect( cabalF );
  238. } catch( Exception e ) {
  239. return null;
  240. }
  241. IDocument doc = provider.getDocument( cabalF );
  242. try {
  243. List<PackageDescriptionStanza> lpds = pd.getStanzas();
  244. for( PackageDescriptionStanza pds: lpds ) {
  245. // Should we change this stanza?
  246. String srcList = pds.getProperties().get(
  247. CabalSyntax.FIELD_HS_SOURCE_DIRS.getCabalName().toLowerCase() );
  248. srcList = srcList == null ? "" : srcList;
  249. List<String> srcs = PackageDescriptionLoader.parseList( srcList );
  250. final Set<IPath> allowedPaths = getStanzaPaths( project, oldFile );
  251. boolean shouldBeChanged = false;
  252. for (String src : srcs) {
  253. IPath srcPath = Path.fromPortableString( src );
  254. if (allowedPaths.contains( srcPath )) {
  255. shouldBeChanged = true;
  256. break;
  257. }
  258. }
  259. if (!shouldBeChanged) {
  260. continue; // We should not
  261. }
  262. // Modules sections
  263. CabalSyntax[] elements = new CabalSyntax[] {
  264. CabalSyntax.FIELD_EXPOSED_MODULES, CabalSyntax.FIELD_OTHER_MODULES };
  265. for( CabalSyntax element: elements ) {
  266. pds = pd.getSameStanza( pds );
  267. String propList = pds.getProperties().get(
  268. element.getCabalName().toLowerCase() );
  269. propList = propList == null ? "" : propList;
  270. List<String> props = PackageDescriptionLoader.parseList( propList );
  271. if( props.indexOf( oldModuleName ) != -1 ) {
  272. RealValuePosition rvp = pds.removeFromPropertyList( element,
  273. oldModuleName );
  274. rvp.updateDocument( doc );
  275. pd = PackageDescriptionLoader.load( doc.get() );
  276. pds = pd.getSameStanza( pds );
  277. rvp = pds.addToPropertyList( element, newModuleName );
  278. rvp.updateDocument( doc );
  279. pd = PackageDescriptionLoader.load( doc.get() );
  280. pds = pd.getSameStanza( pds );
  281. }
  282. }
  283. // Main-is section
  284. String oldMainName = oldModuleName.replace( '.', '/' ) + ".hs";
  285. String newMainName = newModuleName.replace( '.', '/' ) + ".hs";
  286. pds = pd.getSameStanza( pds );
  287. String mainProp = pds.getProperties().get(
  288. CabalSyntax.FIELD_MAIN_IS.getCabalName().toLowerCase() );
  289. mainProp = mainProp == null ? "" : mainProp;
  290. if( oldMainName.equals( mainProp ) ) {
  291. RealValuePosition rvp = pds.update( CabalSyntax.FIELD_MAIN_IS,
  292. newMainName );
  293. rvp.updateDocument( doc );
  294. pd = PackageDescriptionLoader.load( doc.get() );
  295. pds = pd.getSameStanza( pds );
  296. }
  297. }
  298. } catch( Exception e ) {
  299. // Do nothing
  300. } finally {
  301. provider.disconnect( cabalF );
  302. }
  303. return doc.get();
  304. }
  305. public static String newRemoveModuleCabalFile( final IProject project,
  306. final IFile oldFile ) {
  307. TextFileDocumentProvider provider = new TextFileDocumentProvider();
  308. String oldModuleName = getModuleName( oldFile );
  309. IFile cabalF = ScionInstance.getCabalFile( project );
  310. PackageDescription pd = null;
  311. try {
  312. pd = PackageDescriptionLoader.load( cabalF );
  313. provider.connect( cabalF );
  314. } catch( Exception e ) {
  315. return null;
  316. }
  317. IDocument doc = provider.getDocument( cabalF );
  318. try {
  319. List<PackageDescriptionStanza> lpds = pd.getStanzas();
  320. for( PackageDescriptionStanza pds: lpds ) {
  321. // Should we change this stanza?
  322. String srcList = pds.getProperties().get(
  323. CabalSyntax.FIELD_HS_SOURCE_DIRS.getCabalName().toLowerCase() );
  324. srcList = srcList == null ? "" : srcList;
  325. List<String> srcs = PackageDescriptionLoader.parseList( srcList );
  326. final Set<IPath> allowedPaths = getStanzaPaths( project, oldFile );
  327. boolean shouldBeChanged = false;
  328. for (String src : srcs) {
  329. IPath srcPath = Path.fromPortableString( src );
  330. if (allowedPaths.contains( srcPath )) {
  331. shouldBeChanged = true;
  332. break;
  333. }
  334. }
  335. if (!shouldBeChanged) {
  336. continue; // We should not
  337. }
  338. // Modules sections
  339. CabalSyntax[] elements = new CabalSyntax[] {
  340. CabalSyntax.FIELD_EXPOSED_MODULES, CabalSyntax.FIELD_OTHER_MODULES };
  341. for( CabalSyntax element: elements ) {
  342. pds = pd.getSameStanza( pds );
  343. String propList = pds.getProperties().get(
  344. element.getCabalName().toLowerCase() );
  345. propList = propList == null ? "" : propList;
  346. List<String> props = PackageDescriptionLoader.parseList( propList );
  347. if( props.indexOf( oldModuleName ) != -1 ) {
  348. RealValuePosition rvp = pds.removeFromPropertyList( element,
  349. oldModuleName );
  350. rvp.updateDocument( doc );
  351. pd = PackageDescriptionLoader.load( doc.get() );
  352. pds = pd.getSameStanza( pds );
  353. }
  354. }
  355. // Main-is section
  356. String oldMainName = oldModuleName.replace( '.', '/' ) + ".hs";
  357. pds = pd.getSameStanza( pds );
  358. String mainProp = pds.getProperties().get(
  359. CabalSyntax.FIELD_MAIN_IS.getCabalName().toLowerCase() );
  360. mainProp = mainProp == null ? "" : mainProp;
  361. if( oldMainName.equals( mainProp ) ) {
  362. RealValuePosition rvp = pds.update( CabalSyntax.FIELD_MAIN_IS, "" );
  363. rvp.updateDocument( doc );
  364. pd = PackageDescriptionLoader.load( doc.get() );
  365. pds = pd.getSameStanza( pds );
  366. }
  367. }
  368. } catch( Exception e ) {
  369. // Do nothing
  370. } finally {
  371. provider.disconnect( cabalF );
  372. }
  373. return doc.get();
  374. }
  375. public static String newSourceFolderCabalFile(final IProject project,
  376. final IPath oldPath, final IPath newPath) {
  377. TextFileDocumentProvider provider = new TextFileDocumentProvider();
  378. IFile cabalF = ScionInstance.getCabalFile( project );
  379. PackageDescription pd = null;
  380. try {
  381. pd = PackageDescriptionLoader.load( cabalF );
  382. provider.connect( cabalF );
  383. } catch( Exception e ) {
  384. return null;
  385. }
  386. IDocument doc = provider.getDocument( cabalF );
  387. try {
  388. List<PackageDescriptionStanza> lpds = pd.getStanzas();
  389. for( PackageDescriptionStanza pds: lpds ) {
  390. // Should we change this stanza?
  391. String srcList = pds.getProperties().get(
  392. CabalSyntax.FIELD_HS_SOURCE_DIRS.getCabalName().toLowerCase() );
  393. srcList = srcList == null ? "" : srcList;
  394. List<String> srcs = PackageDescriptionLoader.parseList( srcList );
  395. for (String src : srcs) {
  396. IPath srcPath = Path.fromPortableString( src );
  397. if (srcPath.equals( oldPath )) {
  398. RealValuePosition rvp = pds.removeFromPropertyList(
  399. CabalSyntax.FIELD_HS_SOURCE_DIRS,
  400. oldPath.toPortableString() );
  401. rvp.updateDocument( doc );
  402. pd = PackageDescriptionLoader.load( doc.get() );
  403. pds = pd.getSameStanza( pds );
  404. rvp = pds.addToPropertyList(
  405. CabalSyntax.FIELD_HS_SOURCE_DIRS,
  406. newPath.toPortableString() );
  407. rvp.updateDocument( doc );
  408. pd = PackageDescriptionLoader.load( doc.get() );
  409. pds = pd.getSameStanza( pds );
  410. break;
  411. }
  412. }
  413. }
  414. } catch( Exception e ) {
  415. // Do nothing
  416. } finally {
  417. provider.disconnect( cabalF );
  418. }
  419. return doc.get();
  420. }
  421. }