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