001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.util.license;
018
019import java.io.File;
020import java.io.FileFilter;
021import java.util.ArrayList;
022import java.util.HashMap;
023import java.util.List;
024import java.util.Map;
025import java.util.Map.Entry;
026import java.util.Objects;
027
028import org.apache.wicket.util.lang.Generics;
029import org.apache.wicket.util.string.Strings;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033/**
034 * Testcase used in the different wicket projects for testing for the correct ASL license headers.
035 * Doesn't really make sense outside org.apache.wicket.
036 * 
037 * @author Frank Bille Jensen (frankbille)
038 */
039public abstract class ApacheLicenseHeaderTestCase
040{
041        /** Log. */
042        private static final Logger log = LoggerFactory.getLogger(ApacheLicenseHeaderTestCase.class);
043
044        private static final String LINE_ENDING = System.getProperty("line.separator");
045        protected List<String> javaIgnore = Generics.newArrayList();
046        protected List<String> htmlIgnore = Generics.newArrayList();
047        protected List<String> xmlPrologIgnore = Generics.newArrayList();
048        protected List<String> propertiesIgnore = Generics.newArrayList();
049        protected List<String> xmlIgnore = Generics.newArrayList();
050        protected List<String> cssIgnore = Generics.newArrayList();
051        protected List<String> velocityIgnore = Generics.newArrayList();
052        protected List<String> javaScriptIgnore = Generics.newArrayList();
053        protected boolean addHeaders = false;
054        private ILicenseHeaderHandler[] licenseHeaderHandlers;
055        private File baseDirectory = new File("").getAbsoluteFile();
056        /**
057         * Construct.
058         */
059        public ApacheLicenseHeaderTestCase()
060        {
061
062                // -------------------------------
063                // Configure defaults
064                // -------------------------------
065
066                // addHeaders = true;
067                xmlIgnore.add(".settings");
068                xmlIgnore.add("EclipseCodeFormat.xml");
069                xmlIgnore.add("nb-configuration.xml");
070
071                /*
072                 * License header in test files lower the visibility of the test.
073                 */
074                htmlIgnore.add("src/test/java");
075
076                /*
077                 * Low level configuration files for logging. No license needed.
078                 */
079                propertiesIgnore.add("src/test/java");
080
081                /*
082                 * .html in test is very test specific and a license header would confuse and make it
083                 * unclear what the test is about.
084                 */
085                xmlPrologIgnore.add("src/test/java");
086
087                /*
088                 * Ignore package.html
089                 */
090                xmlPrologIgnore.add("package.html");
091        }
092
093        /**
094         *
095         */
096        protected void before()
097        {
098                // setup the base directory for when running inside maven (building a release
099                // comes to mind).
100                String property = System.getProperty("basedir");
101                if (!Strings.isEmpty(property))
102                {
103                        baseDirectory = new File(property).getAbsoluteFile();
104                }
105        }
106
107        /**
108         * Test all the files in the project which has an associated {@link ILicenseHeaderHandler}.
109         */
110        protected void licenseHeaders()
111        {
112                licenseHeaderHandlers = new ILicenseHeaderHandler[] {
113                                new JavaLicenseHeaderHandler(javaIgnore),
114                                new JavaScriptLicenseHeaderHandler(javaScriptIgnore),
115                                new XmlLicenseHeaderHandler(xmlIgnore),
116                                new PropertiesLicenseHeaderHandler(propertiesIgnore),
117                                new HtmlLicenseHeaderHandler(htmlIgnore),
118                                new VelocityLicenseHeaderHandler(velocityIgnore),
119                                new XmlPrologHeaderHandler(xmlPrologIgnore),
120                                new CssLicenseHeaderHandler(cssIgnore), };
121
122                final Map<ILicenseHeaderHandler, List<File>> badFiles = new HashMap<>();
123
124                for (final ILicenseHeaderHandler licenseHeaderHandler : licenseHeaderHandlers)
125                {
126                        visitFiles(licenseHeaderHandler.getSuffixes(), licenseHeaderHandler.getIgnoreFiles(),
127                                new FileVisitor()
128                                {
129                                        @Override
130                                        public void visitFile(final File file)
131                                        {
132                                                if (licenseHeaderHandler.checkLicenseHeader(file) == false)
133                                                {
134                                                        if ((addHeaders == false) ||
135                                                                (licenseHeaderHandler.addLicenseHeader(file) == false))
136                                                        {
137                                                                List<File> files = badFiles.get(licenseHeaderHandler);
138
139                                                                if (files == null)
140                                                                {
141                                                                        files = new ArrayList<>();
142                                                                        badFiles.put(licenseHeaderHandler, files);
143                                                                }
144
145                                                                files.add(file);
146                                                        }
147                                                }
148                                        }
149                                });
150                }
151
152                failIncorrectLicenceHeaders(badFiles);
153        }
154
155        private void failIncorrectLicenceHeaders(final Map<ILicenseHeaderHandler, List<File>> files)
156        {
157                if (files.size() > 0)
158                {
159                        StringBuilder failString = new StringBuilder();
160
161                        for (Entry<ILicenseHeaderHandler, List<File>> entry : files.entrySet())
162                        {
163                                ILicenseHeaderHandler licenseHeaderHandler = entry.getKey();
164                                List<File> fileList = entry.getValue();
165
166                                failString.append('\n');
167                                failString.append(licenseHeaderHandler.getClass().getName());
168                                failString.append(" failed. The following files(");
169                                failString.append(fileList.size());
170                                failString.append(") didn't have correct license header:\n");
171
172                                for (File file : fileList)
173                                {
174                                        String filename = file.getAbsolutePath();
175
176                                        // Find the license type
177                                        String licenseType = licenseHeaderHandler.getLicenseType(file);
178
179                                        failString.append(Objects.requireNonNullElse(licenseType, "NONE"));
180                                        failString.append(' ').append(filename).append(LINE_ENDING);
181                                }
182                        }
183
184                        System.out.println(failString);
185                        throw new AssertionError(failString.toString());
186                }
187        }
188
189        private void visitFiles(final List<String> suffixes, final List<String> ignoreFiles,
190                final FileVisitor fileVisitor)
191        {
192                visitDirectory(suffixes, ignoreFiles, baseDirectory, fileVisitor);
193        }
194
195        private void visitDirectory(final List<String> suffixes, final List<String> ignoreFiles,
196                final File directory, final FileVisitor fileVisitor)
197        {
198                File[] files = directory.listFiles(new SuffixAndIgnoreFileFilter(suffixes, ignoreFiles));
199
200                if (files != null)
201                {
202                        for (File file : files)
203                        {
204                                fileVisitor.visitFile(file);
205                        }
206                }
207
208                // Find the directories in this directory on traverse deeper
209                files = directory.listFiles(new DirectoryFileFilter());
210
211                if (files != null)
212                {
213                        for (File childDirectory : files)
214                        {
215                                visitDirectory(suffixes, ignoreFiles, childDirectory, fileVisitor);
216                        }
217                }
218        }
219
220        interface FileVisitor
221        {
222                /**
223                 * @param file
224                 */
225                void visitFile(File file);
226        }
227
228        private class SuffixAndIgnoreFileFilter implements FileFilter
229        {
230                private final List<String> suffixes;
231                private final List<String> ignoreFiles;
232
233                private SuffixAndIgnoreFileFilter(final List<String> suffixes,
234                        final List<String> ignoreFiles)
235                {
236                        this.suffixes = suffixes;
237                        this.ignoreFiles = ignoreFiles;
238                }
239
240                @Override
241                public boolean accept(final File pathname)
242                {
243                        boolean accept = false;
244
245                        if (pathname.isFile())
246                        {
247                                if (ignoreFile(pathname) == false)
248                                {
249                                        for (String suffix : suffixes)
250                                        {
251                                                if (pathname.getName().endsWith("." + suffix))
252                                                {
253                                                        accept = true;
254                                                        break;
255                                                }
256                                                else
257                                                {
258                                                        log.debug("File ignored: '{}'", pathname);
259                                                }
260                                        }
261                                }
262                                else
263                                {
264                                        log.debug("File ignored: '{}'", pathname);
265                                }
266                        }
267
268                        return accept;
269                }
270
271                private boolean ignoreFile(final File pathname)
272                {
273                        boolean ignore = false;
274
275                        if (ignoreFiles != null)
276                        {
277                                String relativePathname = pathname.getAbsolutePath();
278                                relativePathname = Strings
279                                        .replaceAll(relativePathname,
280                                                baseDirectory.getAbsolutePath() + System.getProperty("file.separator"), "")
281                                        .toString();
282
283                                for (String ignorePath : ignoreFiles)
284                                {
285                                        // Will convert '/'s to '\\'s on Windows
286                                        ignorePath = Strings
287                                                .replaceAll(ignorePath, "/", System.getProperty("file.separator"))
288                                                .toString();
289                                        File ignoreFile = new File(baseDirectory, ignorePath);
290
291                                        // Directory ignore
292                                        if (ignoreFile.isDirectory())
293                                        {
294                                                if (pathname.getAbsolutePath().startsWith(ignoreFile.getAbsolutePath()))
295                                                {
296                                                        ignore = true;
297                                                        break;
298                                                }
299                                        }
300                                        // Absolute file
301                                        else if (ignoreFile.isFile())
302                                        {
303                                                if (relativePathname.equals(ignorePath))
304                                                {
305                                                        ignore = true;
306                                                        break;
307                                                }
308                                        }
309                                        else if (pathname.getName().equals(ignorePath))
310                                        {
311                                                ignore = true;
312                                                break;
313                                        }
314                                }
315                        }
316
317                        return ignore;
318                }
319        }
320
321        private class DirectoryFileFilter implements FileFilter
322        {
323                private final String[] ignoreDirectory = new String[] { ".git" };
324
325                @Override
326                public boolean accept(final File pathname)
327                {
328                        boolean accept = false;
329
330                        if (pathname.isDirectory())
331                        {
332                                String relativePathname = pathname.getAbsolutePath();
333                                relativePathname = Strings
334                                        .replaceAll(relativePathname,
335                                                baseDirectory.getAbsolutePath() + System.getProperty("file.separator"), "")
336                                        .toString();
337                                if ("target".equals(relativePathname) == false)
338                                {
339                                        boolean found = false;
340                                        for (String ignore : ignoreDirectory)
341                                        {
342                                                if (pathname.getName().equals(ignore))
343                                                {
344                                                        found = true;
345                                                        break;
346                                                }
347                                        }
348                                        if (found == false)
349                                        {
350                                                accept = true;
351                                        }
352                                }
353                        }
354
355                        return accept;
356                }
357        }
358}