/** * Copyright (c) 2000-2017 Liferay, Inc. All rights reserved. * * Licensed 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 com.liferay.faces.util.config.internal; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumMap; import java.util.List; import javax.xml.parsers.SAXParser; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import com.liferay.faces.util.logging.Logger; import com.liferay.faces.util.logging.LoggerFactory; import com.liferay.faces.util.xml.internal.SAXHandlerBase; import com.liferay.faces.util.xml.internal.SAXParseCompleteException; /** * @author Neil Griffin */ public class FacesConfigDescriptorParserImpl extends SAXHandlerBase implements FacesConfigDescriptorParser { // Logger private static final Logger logger = LoggerFactory.getLogger(FacesConfigDescriptorParserImpl.class); // Private Constants private static final String ABSOLUTE_ORDERING = "absolute-ordering"; private static final String AFTER = "after"; private static final String BEFORE = "before"; private static final String FACES_CONFIG = "faces-config"; private static final String ORDERING = "ordering"; private static final String OTHERS = "others"; // Private Data Members private String facesConfigName; private List<String> afterNames; private List<String> beforeNames; private boolean isWebInfFacesConfig; private List<String> absoluteOrdering; private Ordering ordering; private boolean parsingAbsoluteOrdering; private boolean parsingAfter; private boolean parsingBefore; private boolean parsingFacesConfig; private boolean parsingName = false; private boolean parsingOrdering; private boolean parsingOthers; private SAXParser saxParser; public FacesConfigDescriptorParserImpl(SAXParser saxParser, boolean resolveEntities) { super(resolveEntities); this.saxParser = saxParser; this.afterNames = new ArrayList<String>(); this.ordering = new OrderingImpl(); this.beforeNames = new ArrayList<String>(); } @Override public void endElement(String uri, String localName, String elementName) throws SAXException { if (parsingFacesConfig) { if (parsingName && !parsingAbsoluteOrdering && !parsingOrdering) { facesConfigName = content.toString().trim(); parsingName = false; } else if (parsingAbsoluteOrdering) { if (parsingName) { if (content != null) { String name = content.toString().trim(); absoluteOrdering.add(name); } parsingName = false; } else if (parsingOthers) { absoluteOrdering.add(Ordering.OTHERS); parsingOthers = false; } else { if (localName.equals(ABSOLUTE_ORDERING)) { parsingAbsoluteOrdering = false; } } } else if (parsingOrdering) { if (parsingBefore) { String beforeName = null; if (parsingName || parsingOthers) { if (parsingOthers) { beforeName = Ordering.OTHERS; beforeNames.add(beforeName); parsingOthers = false; } else { if (content != null) { beforeName = content.toString().trim(); beforeNames.add(beforeName); } parsingName = false; } // System.err.println("endElement: " + localName + ": beforeName = " + beforeName); } else { if (localName.equals(BEFORE)) { parsingBefore = false; if (content != null) { // TODO add {0} parameterization to warn logger.warn("Stray content found when parsing FacesConfig named " + facesConfigName + ". -> Ordering -> before -> content found: " + content + " ... probably belongs inside of a 'name' tag."); logger.warn("Assuming '" + content + "' is a name ..."); beforeName = content.toString().trim(); beforeNames.add(beforeName); } } } } if (parsingAfter) { String afterName = null; if (parsingName || parsingOthers) { if (parsingOthers) { afterName = Ordering.OTHERS; afterNames.add(afterName); parsingOthers = false; } else { if (content != null) { afterName = content.toString().trim(); afterNames.add(afterName); } parsingName = false; } // System.err.println("endElement: " + localName + ": afterName = " + afterName); } else { if (localName.equals(AFTER)) { parsingAfter = false; if (content != null) { // TODO add {0} parameterization to warn logger.warn("Stray content found when parsing FacesConfig named " + facesConfigName + ". -> Ordering -> after -> content found: " + content + " ... probably belongs inside of a 'name' tag."); logger.warn("Assuming '" + content + "' is a name ..."); afterName = content.toString().trim(); afterNames.add(afterName); } } } } if (localName.equals(ORDERING)) { parsingOrdering = false; } } else { if (localName.equals(FACES_CONFIG)) { parsingFacesConfig = false; // TODO the parse method expects this exception ... does this help so late at this point? throw new SAXParseCompleteException(); } super.endElement(uri, localName, elementName); } } content = null; } public boolean isWebInfFacesConfig() { return isWebInfFacesConfig; } @Override public FacesConfigDescriptor parse(InputStream inputStream, String path) throws IOException { try { // see ParseTask in ConfigManager if (path.contains("/WEB-INF/faces-config.xml")) { isWebInfFacesConfig = true; } else { isWebInfFacesConfig = false; } try { saxParser.parse(inputStream, this); } catch (SAXParseCompleteException e) { // ignore -- this indicates cessation of processing when the facesConfigName is discovered. TODO see if // TODO is there a performance benefit here when bailing out early after name and ordering have been // discovered? } // Populate the ordering with routes gathered, if any. if (ordering != null) { EnumMap<Ordering.Path, String[]> routes = ordering.getRoutes(); EnumMap<Ordering.Path, String[]> routesToSet = new EnumMap<Ordering.Path, String[]>( Ordering.Path.class); if (beforeNames.size() > 0) { String[] befores = beforeNames.toArray(new String[beforeNames.size()]); if (beforeNames.size() > 1) { Arrays.sort(befores); } routesToSet.put(Ordering.Path.BEFORE, befores); } else { routesToSet.put(Ordering.Path.BEFORE, routes.get(Ordering.Path.BEFORE)); } if (afterNames.size() > 0) { String[] afters = afterNames.toArray(new String[afterNames.size()]); if (afterNames.size() > 1) { Arrays.sort(afters); } routesToSet.put(Ordering.Path.AFTER, afters); } else { routesToSet.put(Ordering.Path.AFTER, routes.get(Ordering.Path.AFTER)); } ordering.setRoutes(routesToSet); } if ((absoluteOrdering != null) && (absoluteOrdering.size() == 0)) { absoluteOrdering = null; } FacesConfigDescriptor facesConfigDescriptor = new FacesConfigDescriptorImpl(facesConfigName, path, isWebInfFacesConfig, absoluteOrdering, ordering); this.facesConfigName = null; this.absoluteOrdering = null; this.ordering = null; this.afterNames = null; this.beforeNames = null; saxParser.reset(); return facesConfigDescriptor; } catch (SAXException e) { logger.error(e); throw new IOException(e.getMessage()); } } public FacesConfigDescriptor parse(InputStream inputStream, URL url) throws IOException { String path = url.toExternalForm(); return parse(inputStream, path); } @Override public void startElement(String uri, String localName, String elementName, Attributes attributes) throws SAXException { content = new StringBuilder(); if (localName.equals(FACES_CONFIG)) { parsingFacesConfig = true; if (isWebInfFacesConfig()) { this.absoluteOrdering = new ArrayList<String>(); this.ordering = null; this.afterNames = null; this.beforeNames = null; } else { this.absoluteOrdering = null; this.ordering = new OrderingImpl(); this.afterNames = new ArrayList<String>(); this.beforeNames = new ArrayList<String>(); } } else if (localName.equals("name")) { parsingName = true; } else if (localName.equals(ABSOLUTE_ORDERING)) { if (isWebInfFacesConfig()) { parsingAbsoluteOrdering = true; } else { logger.warn("endElement: found " + localName + " section in " + uri.toString() + "\nTrying to ignore this section ..."); } } else if (localName.equals(ORDERING)) { if (isWebInfFacesConfig()) { logger.warn("endElement: found " + localName + " section in " + uri.toString() + "\nTrying to ignore this section ..."); } else { parsingOrdering = true; } } else if (localName.equals(BEFORE)) { parsingBefore = true; } else if (localName.equals(AFTER)) { parsingAfter = true; } // TODO startsWith? really? else if (localName.startsWith(OTHERS)) { parsingOthers = true; } else { super.startElement(uri, localName, elementName, attributes); } } }