001package gudusoft.gsqlparser.lineage.v2.validation; 002 003import java.io.File; 004import java.io.IOException; 005import java.nio.file.Files; 006import java.util.Collections; 007import java.util.LinkedHashMap; 008import java.util.Map; 009 010/** 011 * Where the validator finds the text of a {@code NAMED} source unit to check spans against 012 * (specification Sections 7.8 and 12.2). Without the text, spans are checked for order only and a note says so. 013 * 014 * @since 4.2.17 015 */ 016public interface SourceTextProvider { 017 018 /** No source text: spans are not checked against text. */ 019 SourceTextProvider NONE = new SourceTextProvider() { 020 @Override 021 public byte[] read(String namespace, String logicalName) { 022 return null; 023 } 024 }; 025 026 /** 027 * The bytes of a source unit, or null when this provider does not have them. 028 * 029 * @param namespace the unit's {@code namespace} 030 * @param logicalName the unit's {@code logicalName} 031 */ 032 byte[] read(String namespace, String logicalName); 033 034 /** 035 * A provider that maps namespaces to directories; a logical name is a path relative to its directory, and a 036 * name that is absolute, has a {@code ..} segment or ends with {@code /} is never read. 037 */ 038 final class Directories implements SourceTextProvider { 039 private final Map<String, File> roots; 040 041 public Directories(Map<String, File> roots) { 042 this.roots = Collections.unmodifiableMap(new LinkedHashMap<String, File>(roots)); 043 } 044 045 @Override 046 public byte[] read(String namespace, String logicalName) { 047 File root = roots.get(namespace); 048 // a name that ends with "/" names a directory, never a file, before File drops the separator 049 if (root == null || logicalName == null || logicalName.isEmpty() || logicalName.startsWith("/") 050 || logicalName.endsWith("/")) { 051 return null; 052 } 053 for (String part : logicalName.split("/", -1)) { 054 if ("..".equals(part)) { 055 return null; 056 } 057 } 058 File file = new File(root, logicalName.replace('/', File.separatorChar)); 059 if (!file.isFile()) { 060 return null; 061 } 062 try { 063 return Files.readAllBytes(file.toPath()); 064 } catch (IOException e) { 065 return null; 066 } 067 } 068 } 069}