| // Copyright (C) 2017 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package com.googlesource.gerrit.plugins.findowners; |
| |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Parse lines in an OWNERS file and put them into an OwnersDb. |
| * |
| * <p>OWNERS file syntax:<pre> |
| * lines := (\s* line? \s* "\n")* |
| * line := "set noparent" |
| * | "per-file" \s+ glob \s* "=" \s* directive |
| * | "file:" glob |
| * | comment |
| * | directive |
| * directive := email_address |
| * | "*" |
| * glob := [a-zA-Z0-9_-*?]+ |
| * comment := "#" [^"\n"]* |
| * </pre> |
| * <p> The "file:" directive is not implemented yet. |
| * <p> "per-file glob = directive" applies directive only to files |
| * matching glob. glob does not contain directory path. |
| */ |
| class Parser { |
| private static final Logger log = LoggerFactory.getLogger(Parser.class); |
| |
| static final Pattern PatComment = Pattern.compile("^ *(#.*)?$"); |
| // TODO: have a more precise email address pattern. |
| static final Pattern PatEmail = // email address or a "*" |
| Pattern.compile("^ *([^ <>@]+@[^ <>@#]+|\\*) *(#.*)?$"); |
| static final Pattern PatFile = Pattern.compile("^ *file:.*$"); |
| static final Pattern PatNoParent = |
| Pattern.compile("^ *set +noparent(( |#).*)?$"); |
| static final Pattern PatPerFile = |
| Pattern.compile("^ *per-file +([^= ]+) *= *([^ #]+).*$"); |
| |
| /** |
| * Parse a line in OWNERS file and add info to OwnersDb. |
| * |
| * @param db an OwnersDb to keep parsed info. |
| * @param path the path of OWNERS file. |
| * @param file the OWNERS file path. |
| * @param line the source line. |
| * @param num the line number. |
| * @return error message string or null. |
| */ |
| static String parseLine(OwnersDb db, String path, |
| String file, String line, int num) { |
| // comment and file: directive are parsed but ignored. |
| if (PatNoParent.matcher(line).find()) { |
| db.stopLooking.add(path); |
| return null; |
| } |
| if (PatPerFile.matcher(line).find()) { |
| Matcher m = PatPerFile.matcher(line); |
| m.find(); |
| return parseDirective(db, path + m.group(1), file, m.group(2), num); |
| } |
| if (PatFile.matcher(line).find()) { |
| return warningMsg(file, num, "ignored", line); |
| } |
| // ignore comment and empty lines. |
| return (PatComment.matcher(line).find()) |
| ? null : parseDirective(db, path, file, line, num); |
| } |
| |
| private static String parseDirective(OwnersDb db, String pathGlob, |
| String file, String line, int num) { |
| // A directive is an email address or "*". |
| if (PatEmail.matcher(line).find()) { |
| Matcher m = PatEmail.matcher(line); |
| m.find(); |
| db.addOwnerPathPair(m.group(1), pathGlob); |
| return null; |
| } |
| return errorMsg(file, num, "ignored unknown line", line); |
| } |
| |
| private static String createMsgLine( |
| String prefix, String file, int n, String msg, String line) { |
| return prefix + file + ":" + n + ": " + msg + ": [" + line + "]"; |
| } |
| |
| static String errorMsg(String file, int n, String msg, String line) { |
| return createMsgLine("Error: ", file, n, msg, line); |
| } |
| |
| static String warningMsg(String file, int n, String msg, String line) { |
| return createMsgLine("Warning: ", file, n, msg, line); |
| } |
| } |