View Javadoc

1   package org.apache.maven.doxia.site.decoration.inheritance;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import org.apache.maven.doxia.site.decoration.Banner;
26  import org.apache.maven.doxia.site.decoration.Body;
27  import org.apache.maven.doxia.site.decoration.DecorationModel;
28  import org.apache.maven.doxia.site.decoration.LinkItem;
29  import org.apache.maven.doxia.site.decoration.Logo;
30  import org.apache.maven.doxia.site.decoration.Menu;
31  import org.apache.maven.doxia.site.decoration.MenuItem;
32  
33  import org.codehaus.plexus.component.annotations.Component;
34  import org.codehaus.plexus.util.xml.Xpp3Dom;
35  
36  /**
37   * Manage inheritance of the decoration model.
38   *
39   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
40   * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
41   * @version $Id: DefaultDecorationModelInheritanceAssembler.java 1452051 2013-03-03 15:06:42Z olamy $
42   */
43  @Component( role = DecorationModelInheritanceAssembler.class )
44  public class DefaultDecorationModelInheritanceAssembler
45      implements DecorationModelInheritanceAssembler
46  {
47      /** {@inheritDoc} */
48      public void assembleModelInheritance( String name, DecorationModel child, DecorationModel parent,
49                                            String childBaseUrl, String parentBaseUrl )
50      {
51          // cannot inherit from null parent.
52          if ( parent == null )
53          {
54              return;
55          }
56  
57          URLContainer urlContainer = new URLContainer( parentBaseUrl, childBaseUrl );
58  
59          if ( child.getBannerLeft() == null && parent.getBannerLeft() != null )
60          {
61              child.setBannerLeft( parent.getBannerLeft().clone());
62              rebaseBannerPaths( child.getBannerLeft(), urlContainer );
63          }
64  
65          if ( child.getBannerRight() == null && parent.getBannerRight() != null)
66          {
67              child.setBannerRight( parent.getBannerRight().clone());
68              rebaseBannerPaths( child.getBannerRight(), urlContainer );
69          }
70  
71          if ( child.isDefaultPublishDate() && parent.getPublishDate() != null )
72          {
73              child.setPublishDate( parent.getPublishDate().clone());
74          }
75  
76          if ( child.isDefaultVersion() && parent.getVersion() != null )
77          {
78              child.setVersion( parent.getVersion().clone());
79          }
80  
81          if ( child.getSkin() == null && parent.getSkin() != null )
82          {
83              child.setSkin( parent.getSkin().clone());
84          }
85  
86          child.setPoweredBy( mergePoweredByLists( child.getPoweredBy(), parent.getPoweredBy(), urlContainer ) );
87  
88          if ( parent.getLastModified() > child.getLastModified() )
89          {
90              child.setLastModified( parent.getLastModified() );
91          }
92  
93          assembleBodyInheritance( name, child, parent, urlContainer );
94  
95          assembleCustomInheritance( child, parent );
96      }
97  
98      /** {@inheritDoc} */
99      public void resolvePaths( final DecorationModel decoration, final String baseUrl )
100     {
101         if ( baseUrl == null )
102         {
103             return;
104         }
105 
106         if ( decoration.getBannerLeft() != null )
107         {
108             relativizeBannerPaths( decoration.getBannerLeft(), baseUrl );
109         }
110 
111         if ( decoration.getBannerRight() != null )
112         {
113             relativizeBannerPaths( decoration.getBannerRight(), baseUrl );
114         }
115 
116         for ( Logo logo : decoration.getPoweredBy() )
117         {
118             relativizeLogoPaths( logo, baseUrl );
119         }
120 
121         if ( decoration.getBody() != null )
122         {
123             for ( LinkItem linkItem : decoration.getBody().getLinks() )
124             {
125                 relativizeLinkItemPaths( linkItem, baseUrl );
126             }
127 
128             for ( LinkItem linkItem : decoration.getBody().getBreadcrumbs() )
129             {
130                 relativizeLinkItemPaths( linkItem, baseUrl );
131             }
132 
133             for ( Menu menu : decoration.getBody().getMenus() )
134             {
135                 relativizeMenuPaths( menu.getItems(), baseUrl );
136             }
137         }
138     }
139 
140     /**
141      * Resolves all relative paths between the elements in a banner. The banner element might contain relative paths
142      * to the oldBaseUrl, these are changed to the newBannerUrl.
143      *
144      * @param banner
145      * @param baseUrl
146      */
147     private void relativizeBannerPaths( final Banner banner, final String baseUrl )
148     {
149         // banner has been checked to be not null, both href and src may be empty or null
150         banner.setHref( relativizeLink( banner.getHref(), baseUrl ) );
151         banner.setSrc( relativizeLink( banner.getSrc(), baseUrl ) );
152     }
153 
154     private void rebaseBannerPaths( final Banner banner, final URLContainer urlContainer )
155     {
156         if ( banner.getHref() != null ) // it may be empty
157         {
158             banner.setHref( rebaseLink( banner.getHref(), urlContainer ) );
159         }
160 
161         if ( banner.getSrc() != null )
162         {
163             banner.setSrc( rebaseLink( banner.getSrc(), urlContainer ) );
164         }
165     }
166 
167     private void assembleCustomInheritance( final DecorationModel child, final DecorationModel parent )
168     {
169         if ( child.getCustom() == null )
170         {
171             child.setCustom( parent.getCustom() );
172         }
173         else
174         {
175             child.setCustom( Xpp3Dom.mergeXpp3Dom( (Xpp3Dom) child.getCustom(), (Xpp3Dom) parent.getCustom() ) );
176         }
177     }
178 
179     private void assembleBodyInheritance( final String name, final DecorationModel child, final DecorationModel parent,
180                                           final URLContainer urlContainer )
181     {
182         Body cBody = child.getBody();
183         Body pBody = parent.getBody();
184 
185         if ( cBody != null || pBody != null )
186         {
187             if ( cBody == null )
188             {
189                 cBody = new Body();
190                 child.setBody( cBody );
191             }
192 
193             if ( pBody == null )
194             {
195                 pBody = new Body();
196             }
197 
198             if ( cBody.getHead() == null )
199             {
200                 cBody.setHead( pBody.getHead() );
201             }
202             else
203             {
204                 cBody.setHead( Xpp3Dom.mergeXpp3Dom( (Xpp3Dom) cBody.getHead(), (Xpp3Dom) pBody.getHead() ) );
205             }
206 
207             cBody.setLinks( mergeLinkItemLists( cBody.getLinks(), pBody.getLinks(), urlContainer, false ) );
208 
209             if ( cBody.getBreadcrumbs().isEmpty() && !pBody.getBreadcrumbs().isEmpty() )
210             {
211                 LinkItem breadcrumb = new LinkItem();
212                 breadcrumb.setName( name );
213                 breadcrumb.setHref( "" );
214                 cBody.getBreadcrumbs().add( breadcrumb );
215             }
216             cBody.setBreadcrumbs( mergeLinkItemLists( cBody.getBreadcrumbs(), pBody.getBreadcrumbs(), urlContainer,
217                                                       true ) );
218 
219             cBody.setMenus( mergeMenus( cBody.getMenus(), pBody.getMenus(), urlContainer ) );
220 
221             if ( cBody.getFooter() == null && pBody.getFooter() != null )
222             {
223                 cBody.setFooter( pBody.getFooter() );
224             }
225         }
226     }
227 
228     private List<Menu> mergeMenus( final List<Menu> childMenus, final List<Menu> parentMenus,
229                                    final URLContainer urlContainer )
230     {
231         List<Menu> menus = new ArrayList<Menu>( childMenus.size() + parentMenus.size() );
232 
233         for ( Menu menu : childMenus )
234         {
235             menus.add( menu );
236         }
237 
238         int topCounter = 0;
239         for ( Menu menu : parentMenus )
240         {
241             if ( "top".equals( menu.getInherit() ) )
242             {
243                 final Menu clone = menu.clone();
244 
245                 rebaseMenuPaths( clone.getItems(), urlContainer );
246 
247                 menus.add( topCounter, clone );
248                 topCounter++;
249             }
250             else if ( "bottom".equals( menu.getInherit() ) )
251             {
252                 final Menu clone = menu.clone();
253 
254                 rebaseMenuPaths( clone.getItems(), urlContainer );
255 
256                 menus.add( clone );
257             }
258         }
259 
260         return menus;
261     }
262 
263     private void relativizeMenuPaths( final List<MenuItem> items, final String baseUrl )
264     {
265         for ( MenuItem item : items )
266         {
267             relativizeLinkItemPaths( item, baseUrl );
268             relativizeMenuPaths( item.getItems(), baseUrl );
269         }
270     }
271 
272     private void rebaseMenuPaths( final List<MenuItem> items, final URLContainer urlContainer )
273     {
274         for ( MenuItem item : items )
275         {
276             rebaseLinkItemPaths( item, urlContainer );
277             rebaseMenuPaths( item.getItems(), urlContainer );
278         }
279     }
280 
281     private void relativizeLinkItemPaths( final LinkItem item, final String baseUrl )
282     {
283         item.setHref( relativizeLink( item.getHref(), baseUrl ) );
284     }
285 
286     private void rebaseLinkItemPaths( final LinkItem item, final URLContainer urlContainer )
287     {
288         item.setHref( rebaseLink( item.getHref(), urlContainer ) );
289     }
290 
291     private void relativizeLogoPaths( final Logo logo, final String baseUrl )
292     {
293         logo.setImg( relativizeLink( logo.getImg(), baseUrl ) );
294         relativizeLinkItemPaths( logo, baseUrl );
295     }
296 
297     private void rebaseLogoPaths( final Logo logo, final URLContainer urlContainer )
298     {
299         logo.setImg( rebaseLink( logo.getImg(), urlContainer ) );
300         rebaseLinkItemPaths( logo, urlContainer );
301     }
302 
303     private List<LinkItem> mergeLinkItemLists( final List<LinkItem> childList, final List<LinkItem> parentList,
304                                                final URLContainer urlContainer, boolean cutParentAfterDuplicate )
305     {
306         List<LinkItem> items = new ArrayList<LinkItem>( childList.size() + parentList.size() );
307 
308         for ( LinkItem item : parentList )
309         {
310             if ( !items.contains( item ) && !childList.contains( item ) )
311             {
312                 final LinkItem clone = item.clone();
313 
314                 rebaseLinkItemPaths( clone, urlContainer );
315 
316                 items.add( clone );
317             }
318             else if ( cutParentAfterDuplicate )
319             {
320                 // if a parent item is found in child, ignore next items (case for breadcrumbs)
321                 // merge ( "B > E", "A > B > C > D" ) -> "A > B > E" (notice missing "C > D")
322                 // see http://jira.codehaus.org/browse/DOXIASITETOOLS-62
323                 break;
324             }
325         }
326 
327         for ( LinkItem item : childList )
328         {
329             if ( !items.contains( item ) )
330             {
331                 items.add( item );
332             }
333         }
334 
335         return items;
336     }
337 
338     private List<Logo> mergePoweredByLists( final List<Logo> childList, final List<Logo> parentList,
339                                             final URLContainer urlContainer )
340     {
341         List<Logo> logos = new ArrayList<Logo>( childList.size() + parentList.size() );
342 
343         for ( Logo logo : parentList )
344         {
345             if ( !logos.contains( logo ) )
346             {
347                 final Logo clone = logo.clone();
348 
349                 rebaseLogoPaths( clone, urlContainer );
350 
351                 logos.add( clone );
352             }
353         }
354 
355         for ( Logo logo : childList )
356         {
357             if ( !logos.contains( logo ) )
358             {
359                 logos.add( logo );
360             }
361         }
362 
363         return logos;
364     }
365 
366     // rebase only affects relative links, a relative link wrt an old base gets translated,
367     // so it points to the same location as viewed from a new base
368     private String rebaseLink( final String link, final URLContainer urlContainer )
369     {
370         if ( link == null || urlContainer.getOldPath() == null )
371         {
372             return link;
373         }
374 
375         final URIPathDescriptor oldPath = new URIPathDescriptor( urlContainer.getOldPath(), link );
376 
377         return oldPath.rebaseLink( urlContainer.getNewPath() ).toString();
378     }
379 
380     // relativize only affects absolute links, if the link has the same scheme, host and port
381     // as the base, it is made into a relative link as viewed from the base
382     private String relativizeLink( final String link, final String baseUri )
383     {
384         if ( link == null || baseUri == null )
385         {
386             return link;
387         }
388 
389         // this shouldn't be necessary, just to swallow mal-formed hrefs
390         try
391         {
392             final URIPathDescriptor path = new URIPathDescriptor( baseUri, link );
393 
394             return path.relativizeLink().toString();
395         }
396         catch ( IllegalArgumentException e )
397         {
398             return link;
399         }
400     }
401 
402     /**
403      * Contains an old and a new path.
404      */
405     public final class URLContainer
406     {
407 
408         private final String oldPath;
409 
410         private final String newPath;
411 
412         /**
413          * Construct a URLContainer.
414          *
415          * @param oldPath the old path.
416          * @param newPath the new path.
417          */
418         public URLContainer( final String oldPath, final String newPath )
419         {
420             this.oldPath = oldPath;
421             this.newPath = newPath;
422         }
423 
424         /**
425          * Get the new path.
426          *
427          * @return the new path.
428          */
429         public String getNewPath()
430         {
431             return this.newPath;
432         }
433 
434         /**
435          * Get the old path.
436          *
437          * @return the old path.
438          */
439         public String getOldPath()
440         {
441             return this.oldPath;
442         }
443     }
444 }