| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.maven.doxia.site.decoration.inheritance; |
| |
| import javax.inject.Named; |
| import javax.inject.Singleton; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.apache.maven.doxia.site.decoration.Banner; |
| import org.apache.maven.doxia.site.decoration.Body; |
| import org.apache.maven.doxia.site.decoration.DecorationModel; |
| import org.apache.maven.doxia.site.decoration.LinkItem; |
| import org.apache.maven.doxia.site.decoration.Logo; |
| import org.apache.maven.doxia.site.decoration.Menu; |
| import org.apache.maven.doxia.site.decoration.MenuItem; |
| import org.codehaus.plexus.util.xml.Xpp3Dom; |
| |
| /** |
| * Manage inheritance of the decoration model. |
| * |
| * @author <a href="mailto:brett@apache.org">Brett Porter</a> |
| * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a> |
| */ |
| @Singleton |
| @Named |
| public class DefaultDecorationModelInheritanceAssembler implements DecorationModelInheritanceAssembler { |
| /** {@inheritDoc} */ |
| public void assembleModelInheritance( |
| String name, DecorationModel child, DecorationModel parent, String childBaseUrl, String parentBaseUrl) { |
| if (parent == null || !child.isMergeParent()) { |
| return; |
| } |
| |
| child.setCombineSelf(parent.getCombineSelf()); |
| |
| URLRebaser urlContainer = new URLRebaser(parentBaseUrl, childBaseUrl); |
| |
| if (child.getBannerLeft() == null && parent.getBannerLeft() != null) { |
| child.setBannerLeft(parent.getBannerLeft().clone()); |
| rebaseBannerPaths(child.getBannerLeft(), urlContainer); |
| } |
| |
| if (child.getBannerRight() == null && parent.getBannerRight() != null) { |
| child.setBannerRight(parent.getBannerRight().clone()); |
| rebaseBannerPaths(child.getBannerRight(), urlContainer); |
| } |
| |
| if (child.isDefaultPublishDate() && parent.getPublishDate() != null) { |
| child.setPublishDate(parent.getPublishDate().clone()); |
| } |
| |
| if (child.isDefaultVersion() && parent.getVersion() != null) { |
| child.setVersion(parent.getVersion().clone()); |
| } |
| |
| if (child.getEdit() == null && parent.getEdit() != null) { |
| child.setEdit(parent.getEdit()); |
| } |
| |
| if (child.getSkin() == null && parent.getSkin() != null) { |
| child.setSkin(parent.getSkin().clone()); |
| } |
| |
| child.setPoweredBy(mergePoweredByLists(child.getPoweredBy(), parent.getPoweredBy(), urlContainer)); |
| |
| if (parent.getLastModified() > child.getLastModified()) { |
| child.setLastModified(parent.getLastModified()); |
| } |
| |
| assembleBodyInheritance(name, child, parent, urlContainer); |
| |
| assembleCustomInheritance(child, parent); |
| } |
| |
| /** {@inheritDoc} */ |
| public void resolvePaths(final DecorationModel decoration, final String baseUrl) { |
| if (baseUrl == null) { |
| return; |
| } |
| |
| if (decoration.getBannerLeft() != null) { |
| relativizeBannerPaths(decoration.getBannerLeft(), baseUrl); |
| } |
| |
| if (decoration.getBannerRight() != null) { |
| relativizeBannerPaths(decoration.getBannerRight(), baseUrl); |
| } |
| |
| for (Logo logo : decoration.getPoweredBy()) { |
| relativizeLogoPaths(logo, baseUrl); |
| } |
| |
| if (decoration.getBody() != null) { |
| for (LinkItem linkItem : decoration.getBody().getLinks()) { |
| relativizeLinkItemPaths(linkItem, baseUrl); |
| } |
| |
| for (LinkItem linkItem : decoration.getBody().getBreadcrumbs()) { |
| relativizeLinkItemPaths(linkItem, baseUrl); |
| } |
| |
| for (Menu menu : decoration.getBody().getMenus()) { |
| relativizeMenuPaths(menu.getItems(), baseUrl); |
| } |
| } |
| } |
| |
| /** |
| * Resolves all relative paths between the elements in a banner. The banner element might contain relative paths |
| * to the oldBaseUrl, these are changed to the newBannerUrl. |
| * |
| * @param banner |
| * @param baseUrl |
| */ |
| private void relativizeBannerPaths(final Banner banner, final String baseUrl) { |
| // banner has been checked to be not null, both href and src may be empty or null |
| banner.setHref(relativizeLink(banner.getHref(), baseUrl)); |
| banner.setSrc(relativizeLink(banner.getSrc(), baseUrl)); |
| } |
| |
| private void rebaseBannerPaths(final Banner banner, final URLRebaser urlContainer) { |
| if (banner.getHref() != null) // it may be empty |
| { |
| banner.setHref(urlContainer.rebaseLink(banner.getHref())); |
| } |
| |
| if (banner.getSrc() != null) { |
| banner.setSrc(urlContainer.rebaseLink(banner.getSrc())); |
| } |
| } |
| |
| private void assembleCustomInheritance(final DecorationModel child, final DecorationModel parent) { |
| if (child.getCustom() == null) { |
| child.setCustom(parent.getCustom()); |
| } else { |
| child.setCustom(Xpp3Dom.mergeXpp3Dom((Xpp3Dom) child.getCustom(), (Xpp3Dom) parent.getCustom())); |
| } |
| } |
| |
| private void assembleBodyInheritance( |
| final String name, |
| final DecorationModel child, |
| final DecorationModel parent, |
| final URLRebaser urlContainer) { |
| Body cBody = child.getBody(); |
| Body pBody = parent.getBody(); |
| |
| if (cBody != null || pBody != null) { |
| if (cBody == null) { |
| cBody = new Body(); |
| child.setBody(cBody); |
| } |
| |
| if (pBody == null) { |
| pBody = new Body(); |
| } |
| |
| if (cBody.getHead() == null && pBody.getHead() != null) { |
| cBody.setHead(pBody.getHead()); |
| } |
| |
| cBody.setLinks(mergeLinkItemLists(cBody.getLinks(), pBody.getLinks(), urlContainer, false)); |
| |
| if (cBody.getBreadcrumbs().isEmpty() && !pBody.getBreadcrumbs().isEmpty()) { |
| LinkItem breadcrumb = new LinkItem(); |
| breadcrumb.setName(name); |
| breadcrumb.setHref("index.html"); |
| cBody.getBreadcrumbs().add(breadcrumb); |
| } |
| cBody.setBreadcrumbs( |
| mergeLinkItemLists(cBody.getBreadcrumbs(), pBody.getBreadcrumbs(), urlContainer, true)); |
| |
| cBody.setMenus(mergeMenus(cBody.getMenus(), pBody.getMenus(), urlContainer)); |
| |
| if (cBody.getFooter() == null && pBody.getFooter() != null) { |
| cBody.setFooter(pBody.getFooter()); |
| } |
| } |
| } |
| |
| private List<Menu> mergeMenus( |
| final List<Menu> childMenus, final List<Menu> parentMenus, final URLRebaser urlContainer) { |
| List<Menu> menus = new ArrayList<Menu>(childMenus.size() + parentMenus.size()); |
| |
| for (Menu menu : childMenus) { |
| menus.add(menu); |
| } |
| |
| int topCounter = 0; |
| for (Menu menu : parentMenus) { |
| if ("top".equals(menu.getInherit())) { |
| final Menu clone = menu.clone(); |
| |
| rebaseMenuPaths(clone.getItems(), urlContainer); |
| |
| menus.add(topCounter, clone); |
| topCounter++; |
| } else if ("bottom".equals(menu.getInherit())) { |
| final Menu clone = menu.clone(); |
| |
| rebaseMenuPaths(clone.getItems(), urlContainer); |
| |
| menus.add(clone); |
| } |
| } |
| |
| return menus; |
| } |
| |
| private void relativizeMenuPaths(final List<MenuItem> items, final String baseUrl) { |
| for (MenuItem item : items) { |
| relativizeLinkItemPaths(item, baseUrl); |
| relativizeMenuPaths(item.getItems(), baseUrl); |
| } |
| } |
| |
| private void rebaseMenuPaths(final List<MenuItem> items, final URLRebaser urlContainer) { |
| for (MenuItem item : items) { |
| rebaseLinkItemPaths(item, urlContainer); |
| rebaseMenuPaths(item.getItems(), urlContainer); |
| } |
| } |
| |
| private void relativizeLinkItemPaths(final LinkItem item, final String baseUrl) { |
| item.setHref(relativizeLink(item.getHref(), baseUrl)); |
| } |
| |
| private void rebaseLinkItemPaths(final LinkItem item, final URLRebaser urlContainer) { |
| item.setHref(urlContainer.rebaseLink(item.getHref())); |
| } |
| |
| private void relativizeLogoPaths(final Logo logo, final String baseUrl) { |
| logo.setImg(relativizeLink(logo.getImg(), baseUrl)); |
| relativizeLinkItemPaths(logo, baseUrl); |
| } |
| |
| private void rebaseLogoPaths(final Logo logo, final URLRebaser urlContainer) { |
| logo.setImg(urlContainer.rebaseLink(logo.getImg())); |
| rebaseLinkItemPaths(logo, urlContainer); |
| } |
| |
| private List<LinkItem> mergeLinkItemLists( |
| final List<LinkItem> childList, |
| final List<LinkItem> parentList, |
| final URLRebaser urlContainer, |
| boolean cutParentAfterDuplicate) { |
| List<LinkItem> items = new ArrayList<LinkItem>(childList.size() + parentList.size()); |
| |
| for (LinkItem item : parentList) { |
| if (!items.contains(item) && !childList.contains(item)) { |
| final LinkItem clone = item.clone(); |
| |
| rebaseLinkItemPaths(clone, urlContainer); |
| |
| items.add(clone); |
| } else if (cutParentAfterDuplicate) { |
| // if a parent item is found in child, ignore next items (case for breadcrumbs) |
| // merge ( "B > E", "A > B > C > D" ) -> "A > B > E" (notice missing "C > D") |
| // see https://issues.apache.org/jira/browse/DOXIASITETOOLS-62 |
| break; |
| } |
| } |
| |
| for (LinkItem item : childList) { |
| if (!items.contains(item)) { |
| items.add(item); |
| } |
| } |
| |
| return items; |
| } |
| |
| private List<Logo> mergePoweredByLists( |
| final List<Logo> childList, final List<Logo> parentList, final URLRebaser urlContainer) { |
| List<Logo> logos = new ArrayList<Logo>(childList.size() + parentList.size()); |
| |
| for (Logo logo : parentList) { |
| if (!logos.contains(logo)) { |
| final Logo clone = logo.clone(); |
| |
| rebaseLogoPaths(clone, urlContainer); |
| |
| logos.add(clone); |
| } |
| } |
| |
| for (Logo logo : childList) { |
| if (!logos.contains(logo)) { |
| logos.add(logo); |
| } |
| } |
| |
| return logos; |
| } |
| |
| // relativize only affects absolute links, if the link has the same scheme, host and port |
| // as the base, it is made into a relative link as viewed from the base |
| private String relativizeLink(final String link, final String baseUri) { |
| if (link == null || baseUri == null) { |
| return link; |
| } |
| |
| // this shouldn't be necessary, just to swallow mal-formed hrefs |
| try { |
| final URIPathDescriptor path = new URIPathDescriptor(baseUri, link); |
| |
| return path.relativizeLink().toString(); |
| } catch (IllegalArgumentException e) { |
| return link; |
| } |
| } |
| |
| /** |
| * URL rebaser: based on an old and a new path, can rebase a link based on old path to a value based on the new |
| * path. |
| */ |
| private static class URLRebaser { |
| |
| private final String oldPath; |
| |
| private final String newPath; |
| |
| /** |
| * Construct a URL rebaser. |
| * |
| * @param oldPath the old path. |
| * @param newPath the new path. |
| */ |
| URLRebaser(final String oldPath, final String newPath) { |
| this.oldPath = oldPath; |
| this.newPath = newPath; |
| } |
| |
| /** |
| * Get the new path. |
| * |
| * @return the new path. |
| */ |
| public String getNewPath() { |
| return this.newPath; |
| } |
| |
| /** |
| * Get the old path. |
| * |
| * @return the old path. |
| */ |
| public String getOldPath() { |
| return this.oldPath; |
| } |
| |
| /** |
| * Rebase only affects relative links, a relative link wrt an old base gets translated, |
| * so it points to the same location as viewed from a new base |
| */ |
| public String rebaseLink(final String link) { |
| if (link == null || getOldPath() == null) { |
| return link; |
| } |
| |
| if (link.contains("${project.")) { |
| throw new IllegalArgumentException("site.xml late interpolation ${project.*} expression found" |
| + " in link: '" + link + "'. Use early interpolation ${this.*}"); |
| } |
| |
| final URIPathDescriptor oldPath = new URIPathDescriptor(getOldPath(), link); |
| |
| return oldPath.rebaseLink(getNewPath()).toString(); |
| } |
| } |
| } |