001/* 002 * Copyright 2012-2018 the original author or authors. 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 */ 016 017package org.springframework.boot.cli.command.options; 018 019import java.io.File; 020import java.util.ArrayList; 021import java.util.Collections; 022import java.util.List; 023 024import joptsimple.OptionSet; 025 026import org.springframework.boot.cli.util.ResourceUtils; 027import org.springframework.util.Assert; 028import org.springframework.util.StringUtils; 029 030/** 031 * Extract source file options (anything following '--' in an {@link OptionSet}). 032 * 033 * @author Phillip Webb 034 * @author Dave Syer 035 * @author Greg Turnquist 036 * @author Andy Wilkinson 037 */ 038public class SourceOptions { 039 040 private final List<String> sources; 041 042 private final List<?> args; 043 044 /** 045 * Create a new {@link SourceOptions} instance. 046 * @param options the source option set 047 */ 048 public SourceOptions(OptionSet options) { 049 this(options, null); 050 } 051 052 /** 053 * Create a new {@link SourceOptions} instance. 054 * @param arguments the source arguments 055 */ 056 public SourceOptions(List<?> arguments) { 057 this(arguments, null); 058 } 059 060 /** 061 * Create a new {@link SourceOptions} instance. If it is an error to pass options that 062 * specify non-existent sources, but the default paths are allowed not to exist (the 063 * paths are tested before use). If default paths are provided and the option set 064 * contains no source file arguments it is not an error even if none of the default 065 * paths exist). 066 * @param optionSet the source option set 067 * @param classLoader an optional classloader used to try and load files that are not 068 * found in the local filesystem 069 */ 070 public SourceOptions(OptionSet optionSet, ClassLoader classLoader) { 071 this(optionSet.nonOptionArguments(), classLoader); 072 } 073 074 private SourceOptions(List<?> nonOptionArguments, ClassLoader classLoader) { 075 List<String> sources = new ArrayList<>(); 076 int sourceArgCount = 0; 077 for (Object option : nonOptionArguments) { 078 if (option instanceof String) { 079 String filename = (String) option; 080 if ("--".equals(filename)) { 081 break; 082 } 083 List<String> urls = new ArrayList<>(); 084 File fileCandidate = new File(filename); 085 if (fileCandidate.isFile()) { 086 urls.add(fileCandidate.getAbsoluteFile().toURI().toString()); 087 } 088 else if (!isAbsoluteWindowsFile(fileCandidate)) { 089 urls.addAll(ResourceUtils.getUrls(filename, classLoader)); 090 } 091 for (String url : urls) { 092 if (isSource(url)) { 093 sources.add(url); 094 } 095 } 096 if (isSource(filename)) { 097 if (urls.isEmpty()) { 098 throw new IllegalArgumentException("Can't find " + filename); 099 } 100 else { 101 sourceArgCount++; 102 } 103 } 104 } 105 } 106 this.args = Collections.unmodifiableList( 107 nonOptionArguments.subList(sourceArgCount, nonOptionArguments.size())); 108 Assert.isTrue(!sources.isEmpty(), "Please specify at least one file"); 109 this.sources = Collections.unmodifiableList(sources); 110 } 111 112 private boolean isAbsoluteWindowsFile(File file) { 113 return isWindows() && file.isAbsolute(); 114 } 115 116 private boolean isWindows() { 117 return File.separatorChar == '\\'; 118 } 119 120 private boolean isSource(String name) { 121 return name.endsWith(".java") || name.endsWith(".groovy"); 122 } 123 124 public List<?> getArgs() { 125 return this.args; 126 } 127 128 public String[] getArgsArray() { 129 return this.args.stream().map(this::asString).toArray(String[]::new); 130 } 131 132 private String asString(Object arg) { 133 return (arg != null) ? String.valueOf(arg) : null; 134 } 135 136 public List<String> getSources() { 137 return this.sources; 138 } 139 140 public String[] getSourcesArray() { 141 return StringUtils.toStringArray(this.sources); 142 } 143 144}