1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.databinding.tool.processing;
18 
19 import org.apache.commons.lang3.StringUtils;
20 
21 import android.databinding.tool.store.Location;
22 import android.databinding.tool.util.L;
23 
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.regex.Matcher;
27 import java.util.regex.Pattern;
28 
29 /**
30  * An exception that contains scope information.
31  */
32 public class ScopedException extends RuntimeException {
33     public static final String ERROR_LOG_PREFIX = "****/ data binding error ****";
34     public static final String ERROR_LOG_SUFFIX = "****\\ data binding error ****";
35     public static final String MSG_KEY = "msg:";
36     public static final String LOCATION_KEY = "loc:";
37     public static final String FILE_KEY = "file:";
38     private static boolean sEncodeOutput = false;
39     private ScopedErrorReport mScopedErrorReport;
40     private String mScopeLog;
41 
ScopedException(String message, Object... args)42     public ScopedException(String message, Object... args) {
43         super(message == null ? "unknown data binding exception" : String.format(message, args));
44         mScopedErrorReport = Scope.createReport();
45         mScopeLog = L.isDebugEnabled() ? Scope.produceScopeLog() : null;
46     }
47 
ScopedException(String message, ScopedErrorReport scopedErrorReport)48     ScopedException(String message, ScopedErrorReport scopedErrorReport) {
49         super(message);
50         mScopedErrorReport = scopedErrorReport;
51     }
52 
getBareMessage()53     public String getBareMessage() {
54         return super.getMessage();
55     }
56 
57     @Override
getMessage()58     public String getMessage() {
59         return sEncodeOutput ? createEncodedMessage() : createHumanReadableMessage();
60     }
61 
createHumanReadableMessage()62     private String createHumanReadableMessage() {
63         ScopedErrorReport scopedError = getScopedErrorReport();
64         StringBuilder sb = new StringBuilder();
65         sb.append(super.getMessage()).append("\n")
66                 .append("file://").append(scopedError.getFilePath());
67         if (scopedError.getLocations() != null && scopedError.getLocations().size() > 0) {
68             sb.append(" Line:");
69             sb.append(scopedError.getLocations().get(0).startLine);
70         }
71         sb.append("\n");
72         return sb.toString();
73     }
74 
createEncodedMessage()75     private String createEncodedMessage() {
76         ScopedErrorReport scopedError = getScopedErrorReport();
77         StringBuilder sb = new StringBuilder();
78         sb.append(ERROR_LOG_PREFIX)
79                 .append(MSG_KEY).append(super.getMessage()).append("\n")
80                 .append(FILE_KEY).append(scopedError.getFilePath()).append("\n");
81         if (scopedError.getLocations() != null) {
82             for (Location location : scopedError.getLocations()) {
83                 sb.append(LOCATION_KEY).append(location.toUserReadableString()).append("\n");
84             }
85         }
86         sb.append(ERROR_LOG_SUFFIX);
87         return StringUtils.join(StringUtils.split(sb.toString(), System.lineSeparator()), " ");
88     }
89 
getScopedErrorReport()90     public ScopedErrorReport getScopedErrorReport() {
91         return mScopedErrorReport;
92     }
93 
isValid()94     public boolean isValid() {
95         return mScopedErrorReport.isValid();
96     }
97 
createFromOutput(String output)98     public static ScopedException createFromOutput(String output) {
99         String message = "";
100         String file = "";
101         List<Location> locations = new ArrayList<>();
102         int msgStart = output.indexOf(MSG_KEY);
103         if (msgStart < 0) {
104             message = output;
105         } else {
106             int fileStart = output.indexOf(FILE_KEY, msgStart + MSG_KEY.length());
107             if (fileStart < 0) {
108                 message = output;
109             } else {
110                 message = output.substring(msgStart + MSG_KEY.length(), fileStart);
111                 int locStart = output.indexOf(LOCATION_KEY, fileStart + FILE_KEY.length());
112                 if (locStart < 0) {
113                     file = output.substring(fileStart + FILE_KEY.length());
114                 } else {
115                     file = output.substring(fileStart + FILE_KEY.length(), locStart);
116                     int nextLoc = 0;
117                     while(nextLoc >= 0) {
118                         nextLoc = output.indexOf(LOCATION_KEY, locStart + LOCATION_KEY.length());
119                         Location loc;
120                         if (nextLoc < 0) {
121                             loc = Location.fromUserReadableString(
122                                     output.substring(locStart + LOCATION_KEY.length()));
123                         } else {
124                             loc = Location.fromUserReadableString(
125                                     output.substring(locStart + LOCATION_KEY.length(), nextLoc));
126                         }
127                         if (loc != null && loc.isValid()) {
128                             locations.add(loc);
129                         }
130                         locStart = nextLoc;
131                     }
132                 }
133             }
134         }
135         return new ScopedException(message.trim(),
136                 new ScopedErrorReport(StringUtils.isEmpty(file) ? null : file.trim(), locations));
137     }
138 
extractErrors(String output)139     public static List<ScopedException> extractErrors(String output) {
140         List<ScopedException> errors = new ArrayList<>();
141         int index = output.indexOf(ERROR_LOG_PREFIX);
142         final int limit = output.length();
143         while (index >= 0 && index < limit) {
144             int end = output.indexOf(ERROR_LOG_SUFFIX, index + ERROR_LOG_PREFIX.length());
145             if (end == -1) {
146                 errors.add(createFromOutput(output.substring(index + ERROR_LOG_PREFIX.length())));
147                 break;
148             } else {
149                 errors.add(createFromOutput(output.substring(index + ERROR_LOG_PREFIX.length(),
150                         end)));
151                 index = output.indexOf(ERROR_LOG_PREFIX, end + ERROR_LOG_SUFFIX.length());
152             }
153         }
154         return errors;
155     }
156 
encodeOutput(boolean encodeOutput)157     public static void encodeOutput(boolean encodeOutput) {
158         sEncodeOutput = encodeOutput;
159     }
160 }
161