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