001package org.apache.maven.usability.plugin;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.maven.usability.plugin.io.xpp3.ParamdocXpp3Reader;
023import org.codehaus.plexus.util.ReaderFactory;
024import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
025
026import java.io.BufferedReader;
027import java.io.File;
028import java.io.IOException;
029import java.io.InputStream;
030import java.io.Reader;
031import java.net.MalformedURLException;
032import java.net.URL;
033import java.net.URLClassLoader;
034import java.util.HashMap;
035import java.util.List;
036import java.util.Map;
037
038public class ExpressionDocumenter
039{
040
041    private static final String[] EXPRESSION_ROOTS = { "project", "settings", "session", "plugin", "rootless" };
042
043    private static final String EXPRESSION_DOCO_ROOTPATH = "META-INF/maven/plugin-expressions/";
044
045    private static Map<String, Expression> expressionDocumentation;
046
047    public static Map<String, Expression> load()
048        throws ExpressionDocumentationException
049    {
050        if ( expressionDocumentation == null )
051        {
052            expressionDocumentation = new HashMap<>();
053
054            ClassLoader docLoader = initializeDocLoader();
055
056            for ( String EXPRESSION_ROOT : EXPRESSION_ROOTS )
057            {
058                try ( InputStream docStream = docLoader.getResourceAsStream(
059                    EXPRESSION_DOCO_ROOTPATH + EXPRESSION_ROOT + ".paramdoc.xml" ) )
060                {
061                    if ( docStream != null )
062                    {
063                        Map<String, Expression> doco = parseExpressionDocumentation( docStream );
064
065                        expressionDocumentation.putAll( doco );
066                    }
067                }
068                catch ( IOException e )
069                {
070                    throw new ExpressionDocumentationException(
071                        "Failed to read documentation for expression root: " + EXPRESSION_ROOT, e );
072                }
073                catch ( XmlPullParserException e )
074                {
075                    throw new ExpressionDocumentationException(
076                        "Failed to parse documentation for expression root: " + EXPRESSION_ROOT, e );
077                }
078
079            }
080        }
081
082        return expressionDocumentation;
083    }
084
085    /**
086     * <expressions>
087     * <expression>
088     * <syntax>project.distributionManagementArtifactRepository</syntax>
089     * <origin><![CDATA[
090     * <distributionManagement>
091     * <repository>
092     * <id>some-repo</id>
093     * <url>scp://host/path</url>
094     * </repository>
095     * <snapshotRepository>
096     * <id>some-snap-repo</id>
097     * <url>scp://host/snapshot-path</url>
098     * </snapshotRepository>
099     * </distributionManagement>
100     * ]]></origin>
101     * <usage><![CDATA[
102     * The repositories onto which artifacts should be deployed.
103     * One is for releases, the other for snapshots.
104     * ]]></usage>
105     * </expression>
106     * <expressions>
107     *
108     * @throws IOException
109     * @throws XmlPullParserException
110     */
111    private static Map<String, Expression> parseExpressionDocumentation( InputStream docStream )
112        throws IOException, XmlPullParserException
113    {
114        Reader reader = new BufferedReader( ReaderFactory.newXmlReader( docStream ) );
115
116        ParamdocXpp3Reader paramdocReader = new ParamdocXpp3Reader();
117
118        ExpressionDocumentation documentation = paramdocReader.read( reader, true );
119
120        List<Expression> expressions = documentation.getExpressions();
121
122        Map<String, Expression> bySyntax = new HashMap<>();
123
124        if ( expressions != null && !expressions.isEmpty() )
125        {
126            for ( Expression expression : expressions )
127            {
128                bySyntax.put( expression.getSyntax(), expression );
129            }
130        }
131
132        return bySyntax;
133    }
134
135    private static ClassLoader initializeDocLoader()
136        throws ExpressionDocumentationException
137    {
138        String myResourcePath = ExpressionDocumenter.class.getName().replace( '.', '/' ) + ".class";
139
140        URL myResource = ExpressionDocumenter.class.getClassLoader().getResource( myResourcePath );
141
142        assert myResource != null : "The resource is this class itself loaded by its own classloader and must exist";
143
144        String myClasspathEntry = myResource.getPath();
145
146        myClasspathEntry = myClasspathEntry.substring( 0, myClasspathEntry.length() - ( myResourcePath.length() + 2 ) );
147
148        if ( myClasspathEntry.startsWith( "file:" ) )
149        {
150            myClasspathEntry = myClasspathEntry.substring( "file:".length() );
151        }
152
153        URL docResource;
154        try
155        {
156            docResource = new File( myClasspathEntry ).toURL();
157        }
158        catch ( MalformedURLException e )
159        {
160            throw new ExpressionDocumentationException(
161                "Cannot construct expression documentation classpath" + " resource base.", e );
162        }
163
164        return new URLClassLoader( new URL[]{ docResource } );
165    }
166
167}