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