001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.tools.sourceformatter;
016    
017    import com.liferay.portal.kernel.util.PropertiesUtil;
018    import com.liferay.portal.kernel.util.PropsKeys;
019    import com.liferay.portal.kernel.util.StringBundler;
020    import com.liferay.portal.kernel.util.StringPool;
021    import com.liferay.portal.kernel.util.StringUtil;
022    import com.liferay.portal.kernel.util.Validator;
023    import com.liferay.portal.kernel.xml.Document;
024    import com.liferay.portal.kernel.xml.DocumentException;
025    import com.liferay.portal.kernel.xml.Element;
026    import com.liferay.portal.tools.ComparableRoute;
027    import com.liferay.util.ContentUtil;
028    
029    import java.io.File;
030    import java.io.IOException;
031    
032    import java.util.ArrayList;
033    import java.util.Arrays;
034    import java.util.Collections;
035    import java.util.List;
036    import java.util.Map;
037    import java.util.Properties;
038    import java.util.Set;
039    import java.util.TreeSet;
040    
041    /**
042     * @author Hugo Huijser
043     */
044    public class XMLSourceProcessor extends BaseSourceProcessor {
045    
046            @Override
047            protected void doFormat() throws Exception {
048                    String[] excludes = new String[] {
049                            "**\\.idea\\**", "**\\bin\\**", "**\\classes\\**"
050                    };
051                    String[] includes = new String[] {"**\\*.xml"};
052    
053                    List<String> fileNames = getFileNames(excludes, includes);
054    
055                    for (String fileName : fileNames) {
056                            File file = new File(BASEDIR + fileName);
057    
058                            fileName = StringUtil.replace(
059                                    fileName, StringPool.BACK_SLASH, StringPool.SLASH);
060    
061                            String content = fileUtil.read(file);
062    
063                            String newContent = content;
064    
065                            if (!fileName.contains("/build")) {
066                                    Properties leadingSpacesExclusions = getExclusionsProperties(
067                                            "source_formatter_xml_leading_spaces_exclusions." +
068                                                    "properties");
069    
070                                    String excluded = null;
071    
072                                    if (leadingSpacesExclusions != null) {
073                                            excluded = leadingSpacesExclusions.getProperty(fileName);
074                                    }
075    
076                                    if (excluded == null) {
077                                            newContent = trimContent(newContent, false);
078                                    }
079                            }
080    
081                            if (fileName.contains("/build") && !fileName.contains("/tools/")) {
082                                    newContent = formatAntXML(fileName, newContent);
083                            }
084                            else if (fileName.endsWith("structures.xml")) {
085                                    newContent = formatDDLStructuresXML(newContent);
086                            }
087                            else if (fileName.endsWith("routes.xml")) {
088                                    newContent = formatFriendlyURLRoutesXML(fileName, newContent);
089                            }
090                            else if ((portalSource &&
091                                              fileName.endsWith("/portlet-custom.xml")) ||
092                                             (!portalSource && fileName.endsWith("/portlet.xml"))) {
093    
094                                    newContent = formatPortletXML(newContent);
095                            }
096                            else if (portalSource && fileName.endsWith("/service.xml")) {
097                                    formatServiceXML(fileName, newContent);
098                            }
099                            else if (portalSource && fileName.endsWith("/struts-config.xml")) {
100                                    formatStrutsConfigXML(fileName, content);
101                            }
102                            else if (portalSource && fileName.endsWith("/tiles-defs.xml")) {
103                                    formatTilesDefsXML(fileName, content);
104                            }
105                            else if (portalSource && fileName.endsWith("WEB-INF/web.xml") ||
106                                             !portalSource && fileName.endsWith("/web.xml")) {
107    
108                                    newContent = formatWebXML(fileName, content);
109                            }
110    
111                            if ((newContent != null) && !content.equals(newContent)) {
112                                    fileUtil.write(file, newContent);
113    
114                                    sourceFormatterHelper.printError(fileName, file);
115                            }
116                    }
117            }
118    
119            protected String fixAntXMLProjectName(String fileName, String content) {
120                    int x = 0;
121    
122                    if (fileName.endsWith("-ext/build.xml")) {
123                            if (fileName.startsWith("ext/")) {
124                                    x = 4;
125                            }
126                    }
127                    else if (fileName.endsWith("-hook/build.xml")) {
128                            if (fileName.startsWith("hooks/")) {
129                                    x = 6;
130                            }
131                    }
132                    else if (fileName.endsWith("-layouttpl/build.xml")) {
133                            if (fileName.startsWith("layouttpl/")) {
134                                    x = 10;
135                            }
136                    }
137                    else if (fileName.endsWith("-portlet/build.xml")) {
138                            if (fileName.startsWith("portlets/")) {
139                                    x = 9;
140                            }
141                    }
142                    else if (fileName.endsWith("-theme/build.xml")) {
143                            if (fileName.startsWith("themes/")) {
144                                    x = 7;
145                            }
146                    }
147                    else if (fileName.endsWith("-web/build.xml") &&
148                                     !fileName.endsWith("/ext-web/build.xml")) {
149    
150                            if (fileName.startsWith("webs/")) {
151                                    x = 5;
152                            }
153                    }
154                    else {
155                            return content;
156                    }
157    
158                    int y = fileName.indexOf("/", x);
159    
160                    String correctProjectElementText =
161                            "<project name=\"" + fileName.substring(x, y) + "\"";
162    
163                    if (!content.contains(correctProjectElementText)) {
164                            x = content.indexOf("<project name=\"");
165    
166                            y = content.indexOf("\"", x) + 1;
167                            y = content.indexOf("\"", y) + 1;
168    
169                            content =
170                                    content.substring(0, x) + correctProjectElementText +
171                                            content.substring(y);
172    
173                            processErrorMessage(
174                                    fileName, fileName + " has an incorrect project name");
175                    }
176    
177                    return content;
178            }
179    
180            protected String formatAntXML(String fileName, String content)
181                    throws DocumentException, IOException {
182    
183                    String newContent = trimContent(content, true);
184    
185                    newContent = fixAntXMLProjectName(fileName, newContent);
186    
187                    Document document = saxReaderUtil.read(newContent);
188    
189                    Element rootElement = document.getRootElement();
190    
191                    String previousName = StringPool.BLANK;
192    
193                    List<Element> targetElements = rootElement.elements("target");
194    
195                    for (Element targetElement : targetElements) {
196                            String name = targetElement.attributeValue("name");
197    
198                            if (name.equals("Test")) {
199                                    name = name.toLowerCase();
200                            }
201    
202                            if (name.compareTo(previousName) < -1) {
203                                    processErrorMessage(
204                                            fileName,
205                                            fileName + " has an unordered target " + name);
206    
207                                    break;
208                            }
209    
210                            previousName = name;
211                    }
212    
213                    return newContent;
214            }
215    
216            protected String formatDDLStructuresXML(String content)
217                    throws DocumentException, IOException {
218    
219                    Document document = saxReaderUtil.read(content);
220    
221                    Element rootElement = document.getRootElement();
222    
223                    rootElement.sortAttributes(true);
224    
225                    rootElement.sortElementsByChildElement("structure", "name");
226    
227                    List<Element> structureElements = rootElement.elements("structure");
228    
229                    for (Element structureElement : structureElements) {
230                            Element structureRootElement = structureElement.element("root");
231    
232                            structureRootElement.sortElementsByAttribute(
233                                    "dynamic-element", "name");
234    
235                            List<Element> dynamicElementElements =
236                                    structureRootElement.elements("dynamic-element");
237    
238                            for (Element dynamicElementElement : dynamicElementElements) {
239                                    Element metaDataElement = dynamicElementElement.element(
240                                            "meta-data");
241    
242                                    metaDataElement.sortElementsByAttribute("entry", "name");
243                            }
244                    }
245    
246                    return document.formattedString();
247            }
248    
249            protected String formatFriendlyURLRoutesXML(String fileName, String content)
250                    throws DocumentException, IOException {
251    
252                    Properties friendlyUrlRoutesSortExclusions = getExclusionsProperties(
253                            "source_formatter_friendly_url_routes_sort_exclusions.properties");
254    
255                    String excluded = null;
256    
257                    if (friendlyUrlRoutesSortExclusions != null) {
258                            excluded = friendlyUrlRoutesSortExclusions.getProperty(fileName);
259                    }
260    
261                    if (excluded != null) {
262                            return content;
263                    }
264    
265                    Document document = saxReaderUtil.read(content);
266    
267                    Element rootElement = document.getRootElement();
268    
269                    List<ComparableRoute> comparableRoutes =
270                            new ArrayList<ComparableRoute>();
271    
272                    for (Element routeElement : rootElement.elements("route")) {
273                            String pattern = routeElement.elementText("pattern");
274    
275                            ComparableRoute comparableRoute = new ComparableRoute(pattern);
276    
277                            for (Element generatedParameterElement :
278                                            routeElement.elements("generated-parameter")) {
279    
280                                    String name = generatedParameterElement.attributeValue("name");
281                                    String value = generatedParameterElement.getText();
282    
283                                    comparableRoute.addGeneratedParameter(name, value);
284                            }
285    
286                            for (Element ignoredParameterElement :
287                                            routeElement.elements("ignored-parameter")) {
288    
289                                    String name = ignoredParameterElement.attributeValue("name");
290    
291                                    comparableRoute.addIgnoredParameter(name);
292                            }
293    
294                            for (Element implicitParameterElement :
295                                            routeElement.elements("implicit-parameter")) {
296    
297                                    String name = implicitParameterElement.attributeValue("name");
298                                    String value = implicitParameterElement.getText();
299    
300                                    comparableRoute.addImplicitParameter(name, value);
301                            }
302    
303                            for (Element overriddenParameterElement :
304                                            routeElement.elements("overridden-parameter")) {
305    
306                                    String name = overriddenParameterElement.attributeValue("name");
307                                    String value = overriddenParameterElement.getText();
308    
309                                    comparableRoute.addOverriddenParameter(name, value);
310                            }
311    
312                            comparableRoutes.add(comparableRoute);
313                    }
314    
315                    Collections.sort(comparableRoutes);
316    
317                    StringBundler sb = new StringBundler();
318    
319                    sb.append("<?xml version=\"1.0\"?>\n");
320                    sb.append("<!DOCTYPE routes PUBLIC \"-//Liferay//DTD Friendly URL ");
321                    sb.append("Routes ");
322                    sb.append(mainReleaseVersion);
323                    sb.append("//EN\" \"http://www.liferay.com/dtd/");
324                    sb.append("liferay-friendly-url-routes_");
325                    sb.append(
326                            StringUtil.replace(
327                                    mainReleaseVersion, StringPool.PERIOD, StringPool.UNDERLINE));
328                    sb.append(".dtd\">\n\n<routes>\n");
329    
330                    for (ComparableRoute comparableRoute : comparableRoutes) {
331                            sb.append("\t<route>\n");
332                            sb.append("\t\t<pattern>");
333                            sb.append(comparableRoute.getPattern());
334                            sb.append("</pattern>\n");
335    
336                            Map<String, String> generatedParameters =
337                                    comparableRoute.getGeneratedParameters();
338    
339                            for (Map.Entry<String, String> entry :
340                                            generatedParameters.entrySet()) {
341    
342                                    sb.append("\t\t<generated-parameter name=\"");
343                                    sb.append(entry.getKey());
344                                    sb.append("\">");
345                                    sb.append(entry.getValue());
346                                    sb.append("</generated-parameter>\n");
347                            }
348    
349                            Set<String> ignoredParameters =
350                                    comparableRoute.getIgnoredParameters();
351    
352                            for (String entry : ignoredParameters) {
353                                    sb.append("\t\t<ignored-parameter name=\"");
354                                    sb.append(entry);
355                                    sb.append("\" />\n");
356                            }
357    
358                            Map<String, String> implicitParameters =
359                                    comparableRoute.getImplicitParameters();
360    
361                            for (Map.Entry<String, String> entry :
362                                            implicitParameters.entrySet()) {
363    
364                                    sb.append("\t\t<implicit-parameter name=\"");
365                                    sb.append(entry.getKey());
366                                    sb.append("\">");
367                                    sb.append(entry.getValue());
368                                    sb.append("</implicit-parameter>\n");
369                            }
370    
371                            Map<String, String> overriddenParameters =
372                                    comparableRoute.getOverriddenParameters();
373    
374                            for (Map.Entry<String, String> entry :
375                                            overriddenParameters.entrySet()) {
376    
377                                    sb.append("\t\t<overridden-parameter name=\"");
378                                    sb.append(entry.getKey());
379                                    sb.append("\">");
380                                    sb.append(entry.getValue());
381                                    sb.append("</overridden-parameter>\n");
382                            }
383    
384                            sb.append("\t</route>\n");
385                    }
386    
387                    sb.append("</routes>");
388    
389                    return sb.toString();
390            }
391    
392            protected String formatPortletXML(String content)
393                    throws DocumentException, IOException {
394    
395                    Document document = saxReaderUtil.read(content);
396    
397                    Element rootElement = document.getRootElement();
398    
399                    rootElement.sortAttributes(true);
400    
401                    List<Element> portletElements = rootElement.elements("portlet");
402    
403                    for (Element portletElement : portletElements) {
404                            portletElement.sortElementsByChildElement("init-param", "name");
405    
406                            Element portletPreferencesElement = portletElement.element(
407                                    "portlet-preferences");
408    
409                            if (portletPreferencesElement != null) {
410                                    portletPreferencesElement.sortElementsByChildElement(
411                                            "preference", "name");
412                            }
413                    }
414    
415                    return document.formattedString();
416            }
417    
418            protected void formatServiceXML(String fileName, String content)
419                    throws DocumentException {
420    
421                    Document document = saxReaderUtil.read(content);
422    
423                    Element rootElement = document.getRootElement();
424    
425                    List<Element> entityElements = rootElement.elements("entity");
426    
427                    String previousEntityName = StringPool.BLANK;
428    
429                    for (Element entityElement : entityElements) {
430                            String entityName = entityElement.attributeValue("name");
431    
432                            if (Validator.isNotNull(previousEntityName) &&
433                                    (previousEntityName.compareToIgnoreCase(entityName) > 0)) {
434    
435                                    processErrorMessage(
436                                            fileName, "sort: " + fileName + " " + entityName);
437                            }
438    
439                            String previousReferenceEntity = StringPool.BLANK;
440                            String previousReferencePackagePath = StringPool.BLANK;
441    
442                            List<Element> referenceElements = entityElement.elements(
443                                    "reference");
444    
445                            for (Element referenceElement : referenceElements) {
446                                    String referenceEntity = referenceElement.attributeValue(
447                                            "entity");
448                                    String referencePackagePath = referenceElement.attributeValue(
449                                            "package-path");
450    
451                                    if (Validator.isNotNull(previousReferencePackagePath)) {
452                                            if ((previousReferencePackagePath.compareToIgnoreCase(
453                                                            referencePackagePath) > 0) ||
454                                                    (previousReferencePackagePath.equals(
455                                                            referencePackagePath) &&
456                                                     (previousReferenceEntity.compareToIgnoreCase(
457                                                             referenceEntity) > 0))) {
458    
459                                                    processErrorMessage(
460                                                            fileName,
461                                                            "sort: " + fileName + " " + referencePackagePath);
462                                            }
463                                    }
464    
465                                    previousReferenceEntity = referenceEntity;
466                                    previousReferencePackagePath = referencePackagePath;
467                            }
468    
469                            previousEntityName = entityName;
470                    }
471    
472                    Element exceptionsElement = rootElement.element("exceptions");
473    
474                    if (exceptionsElement == null) {
475                            return;
476                    }
477    
478                    List<Element> exceptionElements = exceptionsElement.elements(
479                            "exception");
480    
481                    String previousException = StringPool.BLANK;
482    
483                    for (Element exceptionElement : exceptionElements) {
484                            String exception = exceptionElement.getStringValue();
485    
486                            if (Validator.isNotNull(previousException) &&
487                                    (previousException.compareToIgnoreCase(exception) > 0)) {
488    
489                                    processErrorMessage(
490                                            fileName, "sort: " + fileName + " " + exception);
491                            }
492    
493                            previousException = exception;
494                    }
495            }
496    
497            protected void formatStrutsConfigXML(String fileName, String content)
498                    throws DocumentException {
499    
500                    Document document = saxReaderUtil.read(content);
501    
502                    Element rootElement = document.getRootElement();
503    
504                    Element actionMappingsElement = rootElement.element("action-mappings");
505    
506                    List<Element> actionElements = actionMappingsElement.elements("action");
507    
508                    String previousPath = StringPool.BLANK;
509    
510                    for (Element actionElement : actionElements) {
511                            String path = actionElement.attributeValue("path");
512    
513                            if (Validator.isNotNull(previousPath) &&
514                                    (previousPath.compareTo(path) > 0) &&
515                                    (!previousPath.startsWith("/portal/") ||
516                                     path.startsWith("/portal/"))) {
517    
518                                    processErrorMessage(fileName, "sort: " + fileName + " " + path);
519                            }
520    
521                            previousPath = path;
522                    }
523            }
524    
525            protected void formatTilesDefsXML(String fileName, String content)
526                    throws DocumentException {
527    
528                    Document document = saxReaderUtil.read(content);
529    
530                    Element rootElement = document.getRootElement();
531    
532                    List<Element> definitionElements = rootElement.elements("definition");
533    
534                    String previousName = StringPool.BLANK;
535    
536                    for (Element definitionElement : definitionElements) {
537                            String name = definitionElement.attributeValue("name");
538    
539                            if (Validator.isNotNull(previousName) &&
540                                    (previousName.compareTo(name) > 0) &&
541                                    !previousName.equals("portlet")) {
542    
543                                    processErrorMessage(fileName, "sort: " + fileName + " " + name);
544    
545                            }
546    
547                            previousName = name;
548                    }
549            }
550    
551            protected String formatWebXML(String fileName, String content)
552                    throws IOException {
553    
554                    if (!portalSource) {
555                            String webXML = ContentUtil.get(
556                                    "com/liferay/portal/deploy/dependencies/web.xml");
557    
558                            if (content.equals(webXML)) {
559                                    processErrorMessage(fileName, fileName);
560                            }
561    
562                            return content;
563                    }
564    
565                    Properties properties = new Properties();
566    
567                    String propertiesContent = fileUtil.read(
568                            BASEDIR + "portal-impl/src/portal.properties");
569    
570                    PropertiesUtil.load(properties, propertiesContent);
571    
572                    String[] locales = StringUtil.split(
573                            properties.getProperty(PropsKeys.LOCALES));
574    
575                    Arrays.sort(locales);
576    
577                    Set<String> urlPatterns = new TreeSet<String>();
578    
579                    for (String locale : locales) {
580                            int pos = locale.indexOf(StringPool.UNDERLINE);
581    
582                            String languageCode = locale.substring(0, pos);
583    
584                            urlPatterns.add(languageCode);
585                            urlPatterns.add(locale);
586                    }
587    
588                    StringBundler sb = new StringBundler();
589    
590                    for (String urlPattern : urlPatterns) {
591                            sb.append("\t<servlet-mapping>\n");
592                            sb.append("\t\t<servlet-name>I18n Servlet</servlet-name>\n");
593                            sb.append("\t\t<url-pattern>/" + urlPattern +"/*</url-pattern>\n");
594                            sb.append("\t</servlet-mapping>\n");
595                    }
596    
597                    int x = content.indexOf("<servlet-mapping>");
598    
599                    x = content.indexOf("<servlet-name>I18n Servlet</servlet-name>", x);
600    
601                    x = content.lastIndexOf("<servlet-mapping>", x) - 1;
602    
603                    int y = content.lastIndexOf(
604                            "<servlet-name>I18n Servlet</servlet-name>");
605    
606                    y = content.indexOf("</servlet-mapping>", y) + 19;
607    
608                    String newContent =
609                            content.substring(0, x) + sb.toString() + content.substring(y);
610    
611                    x = newContent.indexOf("<security-constraint>");
612    
613                    x = newContent.indexOf(
614                            "<web-resource-name>/c/portal/protected</web-resource-name>", x);
615    
616                    x = newContent.indexOf("<url-pattern>", x) - 3;
617    
618                    y = newContent.indexOf("<http-method>", x);
619    
620                    y = newContent.lastIndexOf("</url-pattern>", y) + 15;
621    
622                    sb = new StringBundler();
623    
624                    sb.append("\t\t\t<url-pattern>/c/portal/protected</url-pattern>\n");
625    
626                    for (String urlPattern : urlPatterns) {
627                            sb.append(
628                                    "\t\t\t<url-pattern>/" + urlPattern +
629                                            "/c/portal/protected</url-pattern>\n");
630                    }
631    
632                    return newContent.substring(0, x) + sb.toString() +
633                            newContent.substring(y);
634            }
635    
636    }