001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether.internal.impl.filter.ruletree; 020 021import java.util.Arrays; 022import java.util.List; 023import java.util.concurrent.atomic.AtomicInteger; 024import java.util.stream.Stream; 025 026import static java.util.stream.Collectors.toList; 027 028/** 029 * Prefix tree for paths: if you step on a path prefix that exists, you are good to go. 030 * This class parses a text file that has a prefix on each line: 031 * <ul> 032 * <li>ignored/formatting - each line starting with {@code '#'} (hash) or being empty/blank is ignored.</li> 033 * <li>a path prefix ie "/org/apache/maven".</li> 034 * </ul> 035 * By default, artifact is allowed if layout converted path of it has a matching prefix in this file. 036 * 037 * <p> 038 * Example prefix files: 039 * <ul> 040 * <li><a href="https://repo.maven.apache.org/maven2/.meta/prefixes.txt">Maven Central</a></li> 041 * <li><a href="https://repository.apache.org/content/repositories/releases/.meta/prefixes.txt">ASF Releases</a></li> 042 * <li><a href="https://repo.eclipse.org/content/repositories/tycho/.meta/prefixes.txt">Eclipse Tycho</a></li> 043 * </ul> 044 */ 045public class PrefixTree extends Node<PrefixTree> { 046 private static List<String> elementsOfPath(final String path) { 047 return Arrays.stream(path.split("/")).filter(e -> !e.isEmpty()).collect(toList()); 048 } 049 050 public PrefixTree(String name) { 051 super(name); 052 } 053 054 public int loadNodes(Stream<String> linesStream) { 055 AtomicInteger counter = new AtomicInteger(0); 056 linesStream.forEach(line -> { 057 if (loadNode(line)) { 058 counter.incrementAndGet(); 059 } 060 }); 061 return counter.get(); 062 } 063 064 public boolean loadNode(String line) { 065 if (!line.startsWith("#") && !line.trim().isEmpty()) { 066 PrefixTree currentNode = this; 067 for (String element : elementsOfPath(line)) { 068 currentNode = currentNode.siblings.computeIfAbsent(element, PrefixTree::new); 069 } 070 return true; 071 } 072 return false; 073 } 074 075 public boolean acceptedPath(String path) { 076 final List<String> pathElements = elementsOfPath(path); 077 PrefixTree currentNode = this; 078 for (String pathElement : pathElements) { 079 currentNode = currentNode.siblings.get(pathElement); 080 if (currentNode == null || currentNode.isLeaf()) { 081 break; 082 } 083 } 084 // path is accepted if its elements lead to an existing node that is a leaf node 085 return currentNode != null && currentNode.isLeaf(); 086 } 087}