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