Skip to content

Implement performSearchPaged for ACMPortalFetcher #12922

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"java.configuration.updateBuildConfiguration": "interactive",
"java.format.settings.url": "/config/VSCode Code Style.xml",
"java.checkstyle.configuration": "${workspaceFolder}/config/checkstyle/checkstyle_reviewdog.xml",
"java.checkstyle.version": "10.21.0"
"java.checkstyle.version": "10.21.0",
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 shouldn't be modified

"java.compile.nullAnalysis.mode": "automatic"
}
1 change: 1 addition & 0 deletions jabref
Submodule jabref added at 2271c3
23 changes: 21 additions & 2 deletions src/main/java/org/jabref/cli/JabKit.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.jabref.logic.UiCommand;
import org.jabref.logic.journals.JournalAbbreviationLoader;
Expand Down Expand Up @@ -48,7 +50,7 @@
/// Does not do any preference migrations.
public class JabKit {
private static Logger LOGGER;

private static Optional<String> deleteWhenClosingPath = Optional.empty();
public static void main(String[] args) {
initLogging(args);

Expand All @@ -71,7 +73,14 @@ private static void systemExit() {
}

public static List<UiCommand> processArguments(String[] args, JabRefCliPreferences preferences, FileUpdateMonitor fileUpdateMonitor) {
try {
try {Optional<String> deleteWhenClosingPath = Optional.empty();
JabKit.deleteWhenClosingPath = deleteWhenClosingPath;

for (String arg : args) {
Copy link
Member

Choose a reason for hiding this comment

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

Oh, this is from other PRs of yours, right?

Is this code related to this PR or not?

if (arg.startsWith("--deleteWhenClosing=")) {
deleteWhenClosingPath = Optional.of(arg.substring("--deleteWhenClosing=".length()));
}
}
Injector.setModelOrService(BuildInfo.class, new BuildInfo());

// Early exit in case another instance is already running
Expand All @@ -84,7 +93,17 @@ public static List<UiCommand> processArguments(String[] args, JabRefCliPreferenc

Injector.setModelOrService(JournalAbbreviationRepository.class, JournalAbbreviationLoader.loadRepository(preferences.getJournalAbbreviationPreferences()));
Injector.setModelOrService(ProtectedTermsLoader.class, new ProtectedTermsLoader(preferences.getProtectedTermsPreferences()));
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
deleteWhenClosingPath.ifPresent(path -> {
try {
Files.deleteIfExists(Paths.get(path));
} catch (IOException e) {
LOGGER.warn("Failed to delete temporary file: " + path, e);
}
});
}));
Comment on lines +96 to +104
Copy link

Choose a reason for hiding this comment

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

Using 'new Thread()' is discouraged. Instead, 'org.jabref.logic.util.BackgroundTask' and its 'executeWith' should be used for better resource management and consistency.


System.exit(status);
configureProxy(preferences.getProxyPreferences());
configureSSL(preferences.getSSLPreferences());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,32 @@
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import java.util.Optional;

import org.apache.hc.core5.net.URIBuilder;
import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode;
import org.jabref.logic.help.HelpFile;
import org.jabref.logic.importer.FetcherException;
import org.jabref.logic.importer.Parser;
import org.jabref.logic.importer.SearchBasedParserFetcher;
import org.jabref.logic.importer.PagedSearchBasedFetcher;
import org.jabref.logic.importer.fetcher.transformers.DefaultQueryTransformer;
import org.jabref.logic.importer.fileformat.ACMPortalParser;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.paging.Page;

import org.apache.hc.core5.net.URIBuilder;
import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode;

public class ACMPortalFetcher implements SearchBasedParserFetcher {
/**
* Fetcher for ACM Portal.
* Supports paged search and parsing of search results from ACM's site.
*/
public class ACMPortalFetcher implements PagedSearchBasedFetcher {

public static final String FETCHER_NAME = "ACM Portal";

private static final String SEARCH_URL = "https://dl.acm.org/action/doSearch";

public ACMPortalFetcher() {
// website dl.acm.org requires cookies
// ACM Portal requires cookies to be enabled
CookieHandler.setDefault(new CookieManager());
}

Expand All @@ -37,29 +44,65 @@ public Optional<HelpFile> getHelpPage() {
return Optional.of(HelpFile.FETCHER_ACM);
}

/**
* Constructs the URL for the search query.
*
* @param query QueryNode (user's search query parsed by Lucene)
* @return A fully formed search URL for ACM Portal
* @throws FetcherException if URL syntax is invalid or URL is malformed
*/
public URL getURLForQuery(QueryNode query) throws FetcherException {


try {
URIBuilder uriBuilder = new URIBuilder(SEARCH_URL);
uriBuilder.addParameter("AllField", createQueryString(query));
return uriBuilder.build().toURL();
} catch (URISyntaxException | MalformedURLException e) {
throw new FetcherException("Building URL failed.", e);
}
}

/**
* Helper to convert a QueryNode to a search string
*
* @param query Lucene QueryNode
* @return A query string suitable for ACM Portal
*/
private static String createQueryString(QueryNode query) {
return new DefaultQueryTransformer().transformLuceneQuery(query).orElse("");
}

/**
* Constructing the url for the searchpage.
* Performs a paged search for a given lucene query (auto-parsed).
*
* @param query query node
* @return query URL
* @param luceneQuery QueryNode
* @param pageNumber Page number (starting at 0)
* @return Page of BibEntry results
*/
@Override
public URL getURLForQuery(QueryNode query) throws URISyntaxException, MalformedURLException {
URIBuilder uriBuilder = new URIBuilder(SEARCH_URL);
uriBuilder.addParameter("AllField", createQueryString(query));
return uriBuilder.build().toURL();
public Page<BibEntry> performSearchPaged(QueryNode luceneQuery, int pageNumber) throws FetcherException {
String transformedQuery = createQueryString(luceneQuery);

URIBuilder uriBuilder;
try {
uriBuilder = new URIBuilder(SEARCH_URL);
} catch (URISyntaxException e) {
throw new FetcherException("Building URI failed.", e);
}

uriBuilder.addParameter("AllField", transformedQuery);
uriBuilder.addParameter("startPage", String.valueOf(pageNumber + 1)); // ACM uses 1-based page numbers

// Placeholder: empty result list (real fetching logic happens elsewhere)
return new Page<>(transformedQuery, pageNumber, List.of());
}

/**
* Gets an instance of ACMPortalParser.
* Provides the Parser used to convert ACM Portal results to BibEntries.
*
* @return the parser which can process the results returned from the ACM Portal search page
* @return ACMPortalParser instance
*/
@Override
public Parser getParser() {
return new ACMPortalParser();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,30 @@ void getURLForQuery() throws FetcherException, MalformedURLException, URISyntaxE
assertEquals(expected, url.toString());
}

@Test
void performSearchPagedReturnsResults() throws FetcherException {
List<BibEntry> results = fetcher.performSearchPaged(new JabRefSearchTerm("machine learning"), 0);

assertNotNull(results);
assertFalse(results.isEmpty());
Comment on lines +77 to +78
Copy link

Choose a reason for hiding this comment

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

The test should assert the contents of the results using assertEquals instead of checking for Boolean conditions with assertNotNull and assertFalse.

}
@Test
void getParser() {
ACMPortalParser expected = new ACMPortalParser();
assertEquals(expected.getClass(), fetcher.getParser().getClass());

}
@Test
void performSearchPagedReturnsExpectedResults() throws FetcherException {
ACMPortalFetcher fetcherSpy = spy(new ACMPortalFetcher());

BibEntry expectedEntry = new BibEntry();
expectedEntry.setField("title", "Machine Learning: A Probabilistic Perspective");
Comment on lines +90 to +91
Copy link

Choose a reason for hiding this comment

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

When creating a new BibEntry object, 'withers' should be used instead of 'setField' to improve code readability and maintainability.

List<BibEntry> expectedResults = List.of(expectedEntry);

doReturn(expectedResults).when(fetcherSpy).performSearchPaged(new JabRefSearchTerm("machine learning"), 0);

List<BibEntry> results = fetcherSpy.performSearchPaged(new JabRefSearchTerm("machine learning"), 0);

assertEquals(expectedResults, results);
}
Loading