001/* 002 * Copyright 2020 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.web.source; 017 018import java.io.IOException; 019import java.nio.file.Files; 020import java.nio.file.Path; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.List; 024import java.util.Set; 025import java.util.TreeSet; 026import java.util.stream.Collectors; 027import java.util.stream.Stream; 028 029import org.apache.commons.lang3.StringUtils; 030import org.apache.excalibur.source.Source; 031import org.apache.excalibur.source.SourceException; 032import org.apache.excalibur.source.SourceValidity; 033 034import org.ametys.core.util.path.PathSource; 035 036/** 037 * A skin source is a path source that supports for inheritance 038 */ 039public class SkinSource extends PathSource 040{ 041 /** The files and inherited files {@link _path} is the first existing one */ 042 protected List<Path> _files; 043 044 /** 045 * Builds a SkinSource, given an URI scheme, URI and a Path. 046 * @param scheme The scheme 047 * @param uri the URI 048 * @param files The files (inheritance) Cannot be null nor empty 049 */ 050 public SkinSource(String scheme, String uri, List<Path> files) 051 { 052 super(scheme, uri, _getFirstExistingFile(files)); 053 _files = files; 054 } 055 056 private static Path _getFirstExistingFile(List<Path> files) 057 { 058 for (Path file : files) 059 { 060 if (Files.exists(file)) 061 { 062 return file; 063 } 064 } 065 066 // No one is existing... return the first. 067 return files.get(0); 068 } 069 070 //---------------------------------------------------------------------------------- 071 // TraversableSource interface methods 072 //---------------------------------------------------------------------------------- 073 074 @Override 075 public Source getChild(String name) throws SourceException 076 { 077 if (!Files.isDirectory(_path)) 078 { 079 throw new SourceException(getURI() + " is not a directory"); 080 } 081 082 List<Path> childFiles = _files.stream() 083 .map(p -> p.resolve(name)) 084 .collect(Collectors.toList()); 085 086 return new SkinSource(this.getScheme(), _uri + "/" + name, childFiles); 087 } 088 089 @Override 090 public Collection getChildren() throws SourceException 091 { 092 if (!Files.isDirectory(_path)) 093 { 094 throw new SourceException(getURI() + " is not a directory"); 095 } 096 097 Set<String> children = new TreeSet<>(); 098 for (Path file : _files) 099 { 100 if (Files.exists(file) && Files.isDirectory(file)) 101 { 102 try (Stream<Path> files = Files.list(file)) 103 { 104 files 105 .map(Path::getFileName) 106 .map(Path::toString) 107 .forEach(children::add); 108 } 109 catch (IOException e) 110 { 111 throw new SourceException("Cannot list files under " + file, e); 112 } 113 } 114 } 115 116 Collection<Source> childrenSource = new ArrayList<>(); 117 for (String child : children) 118 { 119 childrenSource.add(getChild(child)); 120 } 121 return childrenSource; 122 } 123 124 @Override 125 public Source getParent() throws SourceException 126 { 127 String parentUri = StringUtils.substringBeforeLast(_uri, "/"); 128 if (parentUri.contains("//")) 129 { 130 List<Path> parentFiles = _files.stream() 131 .map(Path::getParent) 132 .collect(Collectors.toList()); 133 return new SkinSource(getScheme(), parentUri, parentFiles); 134 } 135 else 136 { 137 return null; 138 } 139 } 140 141 @Override 142 public SourceValidity getValidity() 143 { 144 return new SkinTimeStampValidity(_files); 145 } 146 147}