Skip to content

Conversation

@kvitorr
Copy link
Contributor

@kvitorr kvitorr commented Jun 10, 2025

Closes #13274

Allow cygwin-paths on Windows

Create the method convertCygwinPathToWindows in the FileUtil class. It transforms a file path from Cygwin-style to Windows-style only if the operating system is Windows.

Based on research, the two common Cygwin-style formats are:

  • /cygdrive/{drive}/some/path
  • /{drive}/some/path

The third was asked to be added

  • /mnt/{drive}/Users/ (asked in the code review)

The method follows this logic:

/// Converts a Cygwin-style file path to a Windows-style path if the operating system is Windows.
    ///
    /// Supported formats:
    /// - /cygdrive/c/Users/... → C:\Users\...
    /// - /mnt/c/Users/...      → C:\Users\...
    /// - /c/Users/...          → C:\Users\...
    ///
    /// @param filePath the input file path
    /// @return the converted path if running on Windows and path is in Cygwin format; otherwise, returns the original path
    public static Path convertCygwinPathToWindows(String filePath) {
        if (filePath == null) {
            return null;
        }

        if (!OS.WINDOWS) {
            return Path.of(filePath);
        }

        if (filePath.startsWith(MNT_PREFIX) && filePath.length() > 5) {
            return buildWindowsPathWithDriveLetterIndex(filePath, 5);
        }

        if (filePath.startsWith(CYGDRIVE_PREFIX) && filePath.length() > 10) {
            return buildWindowsPathWithDriveLetterIndex(filePath, 10);
        }

        if (ROOT_DRIVE_PATTERN.matcher(filePath).matches()) {
            return buildWindowsPathWithDriveLetterIndex(filePath, 1);
        }

        return Path.of(filePath);
    }

    /// Builds a Windows-style path from a Cygwin-style path using a known prefix index.
    /// @param path the input file path
    /// @param letterIndex the index driver letter, zero-based indexing
    /// @return a windows-style path
    private static Path buildWindowsPathWithDriveLetterIndex(String path, int letterIndex) {
        String driveLetter = path.substring(letterIndex, letterIndex + 1).toUpperCase();
        String windowsPath = path.substring(letterIndex + 1).replace("/", "\\\\");
        return Path.of(driveLetter + ":" + windowsPath);
    }

Additionally, unit tests for this method were added to FileUtilTest class

OBS: I need help to run the second test when Windows is disabled... I am getting an error because the test is not recognized...

    @DisabledOnOs(value = org.junit.jupiter.api.condition.OS.WINDOWS, disabledReason = "Test in others operational systems")
    @ParameterizedTest
    @ValueSource(strings = {"/home/username/Downloads/test.bib"})
    void convertCygwinPathToWindowsShouldReturnOriginalFilePathWhenRunningOnWindows(String filePath) {
        assertEquals(Path.of(filePath), FileUtil.convertCygwinPathToWindows(filePath));
    }

It was created a Converter, called CygwinPathConverter, as requested on the code review... and it was added to the Option Anottation in the classes: CheckConsistency.java, CheckIntegrity.java, Convert.java, Pseudonymize.java, Search.java

Example of use:

@Option(names = {"--input"}, converter = CygWinPathConverter.class, description = "Input BibTeX file", required = true)
    private Path inputFile;

Mandatory checks

  • I own the copyright of the code submitted and I license it under the MIT license
  • Change in CHANGELOG.md described in a way that is understandable for the average user (if change is visible to the user)
  • Tests created for changes (if applicable)
  • Manually tested changed features in running JabRef (always required)
  • [/] Screenshots added in PR description (if change is visible to the user)
  • Checked developer's documentation: Is the information available and up to date? If not, I outlined it in this pull request.
  • Checked documentation: Is the information available and up to date? If not, I created an issue at https://github.com/JabRef/user-documentation/issues or, even better, I submitted a pull request to the documentation repository.

kvitorr added 2 commits June 9, 2025 22:40
Created the method convertCygwinPathToWindows in the FileUtil class. It transforms a file path from Cygwin-style to Windows-style only if the operating system is Windows.

The method follows this logic:
1. If the filePath is null or the OS is not Windows, return it unchanged.
2. If the path starts with /cygdrive/{drive} or /{drive}, replace it with {driveLetterUpperCase}:\ and convert forward slashes to backslashes.

Based on research, the two common Cygwin-style formats are:
- /cygdrive/{drive}/some/path
- /c/some/path

If none of theses patters match, or if the OS is not Windows, the original path is returned.

Additionally, unit tests for this method were added to FileUtilTest class

OBS: I need help to run the second test when Windows is disabled... I am getting an error because the test is not recognized...
…mportException

I need help to understand if I really need to do this change... it was not clear to me on the issue description. (it is my first open source contribuition)
@kvitorr kvitorr changed the title Fix for issue 13274 Fixes #13274: Allow cygwin-paths on Windows Jun 10, 2025
@koppor
Copy link
Member

koppor commented Jun 10, 2025

Please add a CHANGELOG.md entry 😅

@koppor
Copy link
Member

koppor commented Jun 10, 2025

Tests look good!

Now, weave it in into JabKit

String[] data = importArguments.split(",");

String address = data[0];
String address = FileUtil.convertCygwinPathToWindows(data[0]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method 'convertCygwinPathToWindows' should be documented with non-trivial JavaDoc to explain its purpose and usage, as per instruction 23.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, this could be too late - isn't there a picocli transformation helper? Like that at the --input parameter is transformed on picocli level not at the first usage?

@kvitorr
Copy link
Contributor Author

kvitorr commented Jun 10, 2025

@koppor, can you help me? I’m not sure where to create the test for the ArgumentProcessor class. Also, would you like me to rename the test cases?

@koppor
Copy link
Member

koppor commented Jun 10, 2025

@koppor, can you help me? I’m not sure where to create the test for the ArgumentProcessor class. Also, would you like me to rename the test cases?

No need to create tests there. Just implement the transformation of the --input (and --output) filename(s).

@kvitorr
Copy link
Contributor Author

kvitorr commented Jun 10, 2025

@koppor, can you help me? I’m not sure where to create the test for the ArgumentProcessor class. Also, would you like me to rename the test cases?

No need to create tests there. Just implement the transformation of the --input (and --output) filename(s).

already done that in the last commit

@koppor
Copy link
Member

koppor commented Jun 11, 2025

@koppor, can you help me? I’m not sure where to create the test for the ArgumentProcessor class. Also, would you like me to rename the test cases?

No need to create tests there. Just implement the transformation of the --input (and --output) filename(s).

already done that in the last commit

See my comment. Is it possible to move the logic towards the --import argument?

Reason: should also work at pseudonymization and check consistency, not only import. (If I understand the current diff right currently only at the import command it's working. Did you try out for pseudonymize?)

@kvitorr
Copy link
Contributor Author

kvitorr commented Jun 11, 2025

@koppor, can you help me? I’m not sure where to create the test for the ArgumentProcessor class. Also, would you like me to rename the test cases?

No need to create tests there. Just implement the transformation of the --input (and --output) filename(s).

already done that in the last commit

See my comment. Is it possible to move the logic towards the --import argument?

Reason: should also work at pseudonymization and check consistency, not only import. (If I understand the current diff right currently only at the import command it's working. Did you try out for pseudonymize?)

sorry... I didn't see your comment. I'll try to understand what are you saying and then I'll comeback with some commit or comment

@koppor
Copy link
Member

koppor commented Jun 11, 2025

@koppor, can you help me? I’m not sure where to create the test for the ArgumentProcessor class. Also, would you like me to rename the test cases?

No need to create tests there. Just implement the transformation of the --input (and --output) filename(s).

already done that in the last commit

See my comment. Is it possible to move the logic towards the --import argument?

Reason: should also work at pseudonymization and check consistency, not only import. (If I understand the current diff right currently only at the import command it's working. Did you try out for pseudonymize?)

sorry... I didn't see your comment. I'll try to understand what are you saying and then I'll comeback with some commit or comment

Maybe it got lost while I was on the road.

There is a place in the code where --input is mapped to a Java object variable by the picocli annotation @option (or similar). Maybe, there is a transformion possible.

private String inputFormat;

@Option(names = {"--output"}, description = "Output file")
@Option(names = {"--output"}, converter = PathConverter.class, description = "Output file")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of PathConverter for the output file path conversion is not optimal. Consider using Path.of() for better readability and maintainability.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strange that one os PathConverter and the other one is StringPathConverter.

OK, this seems to be generated with AI. Please, instruct the AI to avoid code duplication.

PathConverter is the nicer name, remove the other things.

While scrolling through, you should have seen this for yourself @kvitorr!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this could be artifacts from initial implementation. Should all be Paths.

Copy link
Contributor Author

@kvitorr kvitorr Jun 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@koppor yes, I am using AI to understand the code... I have never worked with picocli before... The converters were made taking into account the type of data that was returned by --input and --output options... So, if there was code duplication it was because there was no consensus on the return type of these variables. I didn't feel comfortable changing the type without knowing the consequences, so I uploaded the changes and waited for the code review.


@ADR(45)
@Option(names = {"--input"}, description = "BibTeX file to be pseudonymized", required = true)
@Option(names = {"--input"}, converter = StringPathConverter.class, description = "BibTeX file to be pseudonymized", required = true)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description should use 'BibTeX' as the spelling for BibTeX in Java strings, as per the guidelines. This ensures consistency across the codebase.

private String inputFile;

@Option(names = {"--output"}, description = "Output pseudo-bib file")
@Option(names = {"--output"}, converter = StringPathConverter.class, description = "Output pseudo-bib file")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description should use 'BibTeX' as the spelling for BibTeX in Java strings, as per the guidelines. This ensures consistency across the codebase.

@kvitorr
Copy link
Contributor Author

kvitorr commented Jun 14, 2025

@koppor @calixtus

I made new changes to the project...

  • I changed the type of the --input and --output attributes from String and File to Path as requested.

  • I removed the code duplication from the converters and changed the name of PathConverter to CygwinPathConverter so that no one gets the impression that it is an unnecessary conversion class, as pointed out by calixtus.

  • I changed the ConvertCygwinPathToWindows method of the FileUtil class to return a Path instead of a String.

  • I created three constants in the FileUtil class, two representing the prefixes "/mnt/" and "/cygdrive/"... the third constant represents the pattern /{driveLetter}/... which are being used within the conditionals.

  • I created a private method within the FileUtil class that is being used within ConvertCygwinPathToWindows... this code snippet was being repeated within the conditionals... and since code duplication was something that was mentioned in the review, I thought it would be better to create this new method.

Other than that, I didn't manually test the changes but I wanted to upload the modification right away so you could give me feedback on whether I understood the requested changes correctly.

I also updated the pull request description.


Regarding the use of AI, I understand your point of view, but I didn't find your comments interesting.

I use it to understand the code, understand dependencies, understand business rules, make comparisons between technologies, understand differences between data types, and try to bring more context to some of the comments you make.

The pull request description was not made using AI, and I understood the changes that were made and also tested it locally.

Yes, there was code duplication, but I didn't understand whether the data type received by the CLI could be changed from String/File to Path. My thought at the time was "if the code is using different data types for input and output, there must be a reason." And that's why I created the three converters, String, File, and Path.

So I thought, I'll upload the change and wait for the review. If I need to change something, it will be requested.

Since this is my FIRST open source contribution, I expected it to be a cool experience. In fact, I'm learning a lot, having contact with organized code, with tests, with documentation and with Picocli, but these comments about the use of AI caught me by surprise.

I read Daniel Roe's text (https://roe.dev/blog/using-ai-in-open-source) and I feel like I didn't deviate from anything he said.

But since this was pointed out to me, I also want to point out something to you, that you at least read the entire description of the pull request and answer any questions written there... this will help clarify ideas, allow a better understanding of the issue and maintain a good environment for both of us.

And, as it is said in the contribution guide, you can reject the PR if you think it doesn't contribute anything to this issue.

@kvitorr kvitorr requested review from calixtus and koppor June 14, 2025 01:59
@calixtus
Copy link
Member

Sorry you got offended by our comment about ai. It's partially a precaution since we get a lot of contributions lately that are completely ai generated, look nice on first sight, but are rubbish on second.

We are not against use of ai, in fact we use it ourselves for repeating task, for simple refactors etc. The general rule is: never to use generated stuff without reading it, testing it and owning it. In short: Never let an ai think for you. Never let an ai speak for you.

There were some indicators, that some of your changes were generated. Maybe it is also our perspective, that makes look changes a newcomer made without much experience so partially similar to changes a LLM does. Eg. Your comments remove line and add line looked like commands one wanted to send to an ai.

Please don't be alienated by our precautions. We're always happy about newcomers and try to be welcoming. This is why we did comment on your changes and not just close the pr without comment because we would have thought that this would just be another ai pr.

@calixtus
Copy link
Member

Looks good to me so far...
is this still a draft or ready for review?

@koppor koppor marked this pull request as ready for review June 15, 2025 10:27
/// @return the converted path if running on Windows and path is in Cygwin format; otherwise, returns the original path
public static Path convertCygwinPathToWindows(String filePath) {
if (filePath == null) {
return null;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New public methods should not return null. The method should return Optional instead to better handle the null case and follow Java best practices.

return Path.of(filePath);
}

/// Builds a Windows-style path from a Cygwin-style path using a known prefix index.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Triple-slash comments are used instead of proper JavaDoc format. Additionally, the comment is trivial and doesn't add new information beyond what the code shows.

Copy link
Member

@koppor koppor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small comments.

Please also have a look at the failing jbang tests - maybe, some interface changed and needs to be adapted?

https://github.com/JabRef/jabref/actions/runs/15646674264/job/44085291757?pr=13297

@@ -0,0 +1,16 @@
package org.jabref.cli.converter;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For naming, I think, this should be with "s", because there will be more than one converters in the future.

pacakge org.jabref.cli.converters.

requires org.tinylog.impl;

requires java.xml;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not modify lines of code not related to the PR if not 90% sure

Here, the empty line does make sence to separate blocks. Please undo this change.

///
/// @param filePath the input file path
/// @return the converted path if running on Windows and path is in Cygwin format; otherwise, returns the original path
public static Path convertCygwinPathToWindows(String filePath) {
Copy link
Member

@koppor koppor Jun 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method also works on linux - proposal for another name:

Suggested change
public static Path convertCygwinPathToWindows(String filePath) {
public static Path convertStringToPathRespectingCygwin(String filePath) {

return Arrays.binarySearch(ILLEGAL_CHARS, c) < 0;
}

/// Converts a Cygwin-style file path to a Windows-style path if the operating system is Windows.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add something like "plainly converted if on non-Windows"

@koppor
Copy link
Member

koppor commented Jul 4, 2025

@kvitorr Do you intend to continue working on this? I think, it is "just" about addressing my small comments, fix jbang and merge main branch.

@koppor
Copy link
Member

koppor commented Jul 28, 2025

@kvitorr Will you continue here?

@calixtus
Copy link
Member

Hi @kvitorr i tried to merge current main into your PR and finish it, but you have your branch protected against maintainer pushes. Please either finish the PR yourself or allow maintainer pushes (checkbox in the pr visible to you) so we can finish it.
Otherwise we would have to close this PR, which would be a real shame, as the code looks good (except small remarks by koppor) and the addition is really good.

@kvitorr
Copy link
Contributor Author

kvitorr commented Aug 22, 2025

Hi @kvitorr i tried to merge current main into your PR and finish it, but you have your branch protected against maintainer pushes. Please either finish the PR yourself or allow maintainer pushes (checkbox in the pr visible to you) so we can finish it. Otherwise we would have to close this PR, which would be a real shame, as the code looks good (except small remarks by koppor) and the addition is really good.

sorry, I was not accessing the email that is connected to my github and did not see the comments on the PR. I'll will make the changes requested.

koppor
koppor previously approved these changes Sep 3, 2025
Copy link
Member

@koppor koppor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, we can fix this nitpicks later.

@jabref-machine
Copy link
Collaborator

Note that your PR will not be reviewed/accepted until you have gone through the mandatory checks in the description and marked each of them them exactly in the format of [x] (done), [ ] (not done yet) or [/] (not applicable).

@trag-bot
Copy link

trag-bot bot commented Sep 3, 2025

@trag-bot didn't find any issues in the code! ✅✨

Copy link
Member

@koppor koppor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, its good enough

Future work might be to make it available for JabLS and JabSrv. CC @palukku

@koppor koppor enabled auto-merge September 3, 2025 21:27
@koppor koppor added this pull request to the merge queue Sep 3, 2025
Merged via the queue into JabRef:main with commit 39ae99e Sep 3, 2025
56 of 57 checks passed
Siedlerchr added a commit that referenced this pull request Sep 8, 2025
* upstream/main: (54 commits)
  Split relativizeSymlinks parameterized tests in separate tests (#13782)
  Update the search syntax highlight for web search (#13801)
  Chore(deps): Bump ai.djl:bom from 0.33.0 to 0.34.0 in /versions (#13833)
  Fix typos in CHANGELOG.md (#13826)
  Chore(deps): Bump com.konghq:unirest-modules-gson in /versions (#13831)
  Chore(deps): Bump org.gradlex:extra-java-module-info in /build-logic (#13830)
  Chore(deps): Bump org.apache.logging.log4j:log4j-to-slf4j in /versions (#13832)
  Chore(deps): Bump io.zonky.test.postgres:embedded-postgres-binaries-bom (#13834)
  Chore(deps): Bump jablib/src/main/resources/csl-locales (#13829)
  Chore(deps): Bump jablib/src/main/resources/csl-styles (#13827)
  Chore(deps): Bump jablib/src/main/abbrv.jabref.org (#13828)
  add: CAYW endpoint formats (#13785)
  New Crowdin updates (#13823)
  chore(deps): update dependency org.kohsuke:github-api to v2.0-rc.5 (#13822)
  Add support for automatic ICORE conference ranking lookup [#13476] (#13699)
  New Crowdin updates (#13820)
  Initialize search bar auto-completion with real database context (no tab switch needed) (#13816)
  Fixes #13274: Allow cygwin-paths on Windows (#13297)
  Refine "REDACTED" replacement of API key value in web fetcher search URL (#13814)
  changed ISSNCleanup into NormalizeIssn, refactored respective tests #13748 (#13767)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow cygwin-paths on Windows

4 participants