Skip to content
New issue

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

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

Already on GitHub? # to your account

Closes #908 - Added functionality to retrieve the first line of a match. #911

Merged
merged 5 commits into from
Aug 13, 2020
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,9 @@ public class FileController extends FileBaseController {
@ApiOperation(value = "Write a file", notes = "Creates or overwrites a file with the provided text content")
@ApiImplicitParam(name = "Path", type = "string", value = "The part of the url after /files/ defines the path to the file to write.")
@PutMapping(value = "files/**")
public void writeFile(HttpServletRequest request,
@ApiParam("If true, the request body is not parsed as json and is instead written directly to" + " the result file.")
@RequestParam(defaultValue = "false")
boolean raw,
@ApiParam(value = "The content to write, either raw or a json", examples = @Example(value = {@ExampleProperty(mediaType = "application/json", value = "{ 'content' : 'This is the" + " file content' }"), @ExampleProperty(mediaType = "text/plain", value = "This is the file content")}))

@RequestBody(required = false)
String content) throws IOException {
public void writeFile(HttpServletRequest request, @ApiParam("If true, the request body is not parsed as json and is instead written directly to" + " the result file.") @RequestParam(defaultValue = "false") boolean raw, @ApiParam(value = "The content to write, either raw or a json", examples = @Example(value = {@ExampleProperty(mediaType = "application/json", value = "{ 'content' : 'This is the" + " file content' }"), @ExampleProperty(mediaType = "text/plain", value = "This is the file content")}))

@RequestBody(required = false) String content) throws IOException {
String path = RequestUtil.getRequestSubPath(request);

String fileContent;
Expand All @@ -60,10 +55,7 @@ public void writeFile(HttpServletRequest request,

@ExampleProperty(mediaType = "text/plain", value = "This is the file content")}))
@GetMapping(value = "files/**")
public Object readFile(HttpServletRequest request,
@ApiParam("If true, the response body is not formatted as json and is instead the plain text" + " content of the file.")
@RequestParam(defaultValue = "false")
boolean raw) {
public Object readFile(HttpServletRequest request, @ApiParam("If true, the response body is not formatted as json and is instead the plain text" + " content of the file.") @RequestParam(defaultValue = "false") boolean raw) {
String path = RequestUtil.getRequestSubPath(request);
Optional<String> contentOptional = fileManager.getWorkingDirectory().readConfigurationFile(path);

Expand All @@ -90,10 +82,10 @@ public void deleteFile(HttpServletRequest request) throws IOException {
fileManager.getWorkingDirectory().deleteConfiguration(path);
}

@ApiOperation(value = "Search the given query in all present files.", notes = "Searches the given query in all present files. " + "Searches for as many matches as defined by the limit parameter. If the the limit is set " + "to -1, the query is searched for all occurrences in all files. All found matches are " + "returned in a list of SearchResult instances. Each of these instances contains the " + "following variables:" + "<p>" + "<b>file:</b> a String resembling the name of the file the match was found in." + "<p>" + "<b>startLine:</b> the number of the line in this file where the found match starts as " + "integer." + "<p>" + "<b>endLine:</b> the number of the line in this file where the found match ends as integer." + "<p>" + "<b>startColumn:</b> the number of the column where the found found match starts as " + "integer." + "<p>" + "<b>endColumn:</b> the number of the column where the found match ends as integer.")
@ApiImplicitParams({@ApiImplicitParam(name = "query", value = "The query string that should be searched in the files."), @ApiImplicitParam(name = "limit", value = "The limit for the returned values. Use '-1' for no limit.")})
@ApiOperation(value = "Search the given query in all present files.", notes = "Searches the given query in all present files. " + "Searches for as many matches as defined by the limit parameter. If the the limit is set " + "to -1, the query is searched for all occurrences in all files. All found matches are " + "returned in a list of SearchResult instances. Each of these instances contains the " + "following variables:" + "<p>" + "<b>file:</b> a String resembling the name of the file the match was found in." + "<p>" + "<b>firstLine:</b> the first line of the found match. Only retrieved if retrieveFirstLine is true " + "<p>" + "<b>startLine:</b> the number of the line in this file where the found match starts as " + "integer." + "<p>" + "<b>endLine:</b> the number of the line in this file where the found match ends as integer." + "<p>" + "<b>startColumn:</b> the number of the column where the found found match starts as " + "integer." + "<p>" + "<b>endColumn:</b> the number of the column where the found match ends as integer.")
@ApiImplicitParams({@ApiImplicitParam(name = "query", value = "The query string that should be searched in the files."), @ApiImplicitParam(name = "limit", value = "The limit for the returned values. Use '-1' for no limit."), @ApiImplicitParam(name = "include-first-line", value = "If true, the first line of each match is added to the search results.")})
@GetMapping(value = "search")
public List<SearchResult> searchForContent(@RequestParam String query, @RequestParam(defaultValue = "100") int limit) {
return fileContentSearchEngine.search(query, limit);
public List<SearchResult> searchForContent(@RequestParam String query, @RequestParam(defaultValue = "100") int limit, @RequestParam(name = "include-first-line", defaultValue = "false") boolean includeFirstLine) {
return fileContentSearchEngine.search(query, limit, includeFirstLine);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,31 +33,33 @@ public class FileContentSearchEngine {
* <p>
* If the given query is an empty String, an empty Map is returned.
*
* @param query The String that is searched for.
* @param limit The maximum amount of entries that should be searched. Set to -1 to get all entries returned.
* @param query The String that is searched for.
* @param limit The maximum amount of entries that should be searched. Set to -1 to get all entries returned.
* @param retrieveFirstLine If true, the first line of a match is added to the SearchResult
*
* @return A List containing {@link SearchResult} instances for each found match.
*/
public List<SearchResult> search(String query, int limit) {
public List<SearchResult> search(String query, int limit, boolean retrieveFirstLine) {
if (query.isEmpty()) {
return Collections.emptyList();
}

return search(query, limit, fileManager.getWorkspaceRevision());
return search(query, limit, fileManager.getWorkspaceRevision(), retrieveFirstLine);
}

/**
* Searches in all configuration files which can be accessed by the given {@link RevisionAccess} for the specified
* query string. The amount of results can be limited using the limit argument.
*
* @param query the query string to look for
* @param limit the maximum amount of results
* @param revisionAccess the accessor for fetching the files
* @param query the query string to look for
* @param limit the maximum amount of results
* @param revisionAccess the accessor for fetching the files
* @param retrieveFirstLine If true, the first line of a match is added to the SearchResult
*
* @return a list of {@link SearchResult} representing the matches
*/
private List<SearchResult> search(String query, int limit, RevisionAccess revisionAccess) {
Pattern queryPattern = Pattern.compile(Pattern.quote(query));
private List<SearchResult> search(String query, int limit, RevisionAccess revisionAccess, boolean retrieveFirstLine) {
Pattern queryPattern = Pattern.compile(Pattern.quote(query), Pattern.CASE_INSENSITIVE);
List<FileInfo> files = revisionAccess.listConfigurationFiles("");

AtomicInteger limitCounter = new AtomicInteger(limit);
Expand All @@ -67,7 +69,7 @@ private List<SearchResult> search(String query, int limit, RevisionAccess revisi
.reduce(Stream.empty(), Stream::concat)
.map(filename -> {
Optional<String> content = revisionAccess.readConfigurationFile(filename);
return content.map(fileContent -> findQuery(filename, fileContent, queryPattern, limitCounter));
return content.map(fileContent -> findQuery(filename, fileContent, queryPattern, limitCounter, retrieveFirstLine));
})
.filter(Optional::isPresent)
.map(Optional::get)
Expand All @@ -81,14 +83,15 @@ private List<SearchResult> search(String query, int limit, RevisionAccess revisi
* Searches in the specified content for the specified query pattern. The passed content represents the content of the
* file with the specified name.
*
* @param fileName the filename of the current file
* @param content the file's content
* @param queryPattern the pattern to search for
* @param limitCounter the amount of results to add
* @param fileName the filename of the current file
* @param content the file's content
* @param queryPattern the pattern to search for
* @param limitCounter the amount of results to add
* @param retrieveFirstLine If true, the first line of a match is added to the SearchResult
*
* @return a list of {@link SearchResult} representing the matches
*/
private List<SearchResult> findQuery(String fileName, String content, Pattern queryPattern, AtomicInteger limitCounter) {
private List<SearchResult> findQuery(String fileName, String content, Pattern queryPattern, AtomicInteger limitCounter, boolean retrieveFirstLine) {
if (StringUtils.isEmpty(content)) {
return Collections.emptyList();
}
Expand All @@ -102,23 +105,29 @@ private List<SearchResult> findQuery(String fileName, String content, Pattern qu

Matcher matcher = queryPattern.matcher(content);
while (matcher.find() && limitCounter.decrementAndGet() >= 0) {
SearchResult.SearchResultBuilder searchResultBuilder = SearchResult.builder().file(fileName);
int start = matcher.start();
int end = matcher.end();

while (start >= currentLine.getEndIndex()) {
currentLine = listIterator.next();
}
int startLine = currentLine.getLineNumber();
int relativeStart = start - currentLine.getStartIndex();
searchResultBuilder.startLine(currentLine.getLineNumber());
searchResultBuilder.startColumn(start - currentLine.getStartIndex());

if (retrieveFirstLine) {
String firstLine = content.substring(currentLine.getStartIndex(), currentLine.getEndIndex());
firstLine = firstLine.replace("\n", "").replace("\r", "");
searchResultBuilder.firstLine(firstLine);
}

while (end > currentLine.getEndIndex()) {
currentLine = listIterator.next();
}
int endLine = currentLine.getLineNumber();
int relativeEnd = end - currentLine.getStartIndex();
searchResultBuilder.endLine(currentLine.getLineNumber());
searchResultBuilder.endColumn(end - currentLine.getStartIndex());

SearchResult result = new SearchResult(fileName, startLine, relativeStart, endLine, relativeEnd);
results.add(result);
results.add(searchResultBuilder.build());
}

return results;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,33 @@
package rocks.inspectit.ocelot.search;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* This class resembles a query matched by the FileContentSearchEngine. The file variable contains the file name the
* query was found in. The start variables provide information on in which line and on which column in this line a
* searched substring was found. The end variable does alike for the end of the query.
*/
@AllArgsConstructor
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class SearchResult {

/**
* The name of the file the match was found in.
*/
private String file;

/**
* The first line of the match.
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
private String firstLine;

/**
* The start line of the match.
*/
Expand Down
Loading