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 
20 import com.google.common.base.Joiner;
21 import com.google.common.base.Splitter;
22 import com.google.common.base.Strings;
23 
24 import android.databinding.tool.store.Location;
25 import android.databinding.tool.util.L;
26 import android.databinding.tool.util.StringUtils;
27 
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.regex.Matcher;
31 import java.util.regex.Pattern;
32 
33 /**
34  * An exception that contains scope information.
35  */
36 public class ScopedException extends RuntimeException {
37     public static final String ERROR_LOG_PREFIX = "****/ data binding error ****";
38     public static final String ERROR_LOG_SUFFIX = "****\\ data binding error ****";
39     public static final String MSG_KEY = "msg:";
40     public static final String LOCATION_KEY = "loc:";
41     public static final String FILE_KEY = "file:";
42     private static boolean sEncodeOutput = false;
43     private ScopedErrorReport mScopedErrorReport;
44     private String mScopeLog;
45 
ScopedException(String message, Object... args)46     public ScopedException(String message, Object... args) {
47         super(message == null ? "unknown data binding exception" :
48                 args.length == 0 ? message : String.format(message, args));
49         mScopedErrorReport = Scope.createReport();
50         mScopeLog = L.isDebugEnabled() ? Scope.produceScopeLog() : null;
51     }
52 
ScopedException(String message, ScopedErrorReport scopedErrorReport)53     ScopedException(String message, ScopedErrorReport scopedErrorReport) {
54         super(message);
55         mScopedErrorReport = scopedErrorReport;
56     }
57 
getBareMessage()58     public String getBareMessage() {
59         return super.getMessage();
60     }
61 
62     @Override
getMessage()63     public String getMessage() {
64         return sEncodeOutput ? createEncodedMessage() : createHumanReadableMessage();
65     }
66 
createHumanReadableMessage()67     public String createHumanReadableMessage() {
68         ScopedErrorReport scopedError = getScopedErrorReport();
69         StringBuilder sb = new StringBuilder();
70         sb.append(super.getMessage()).append("\n")
71                 .append("file://").append(scopedError.getFilePath());
72         if (scopedError.getLocations() != null && scopedError.getLocations().size() > 0) {
73             sb.append(" Line:");
74             sb.append(scopedError.getLocations().get(0).startLine);
75         }
76         sb.append("\n");
77         return sb.toString();
78     }
79 
createEncodedMessage()80     private String createEncodedMessage() {
81         ScopedErrorReport scopedError = getScopedErrorReport();
82         StringBuilder sb = new StringBuilder();
83         sb.append(ERROR_LOG_PREFIX)
84                 .append(MSG_KEY).append(super.getMessage()).append("\n")
85                 .append(FILE_KEY).append(scopedError.getFilePath()).append("\n");
86         if (scopedError.getLocations() != null) {
87             for (Location location : scopedError.getLocations()) {
88                 sb.append(LOCATION_KEY).append(location.toUserReadableString()).append("\n");
89             }
90         }
91         sb.append(ERROR_LOG_SUFFIX);
92         return Joiner.on(' ').join(Splitter.on(StringUtils.LINE_SEPARATOR).split(sb));
93     }
94 
getScopedErrorReport()95     public ScopedErrorReport getScopedErrorReport() {
96         return mScopedErrorReport;
97     }
98 
isValid()99     public boolean isValid() {
100         return mScopedErrorReport.isValid();
101     }
102 
createFromOutput(String output)103     public static ScopedException createFromOutput(String output) {
104         String message = "";
105         String file = "";
106         List<Location> locations = new ArrayList<Location>();
107         int msgStart = output.indexOf(MSG_KEY);
108         if (msgStart < 0) {
109             message = output;
110         } else {
111             int fileStart = output.indexOf(FILE_KEY, msgStart + MSG_KEY.length());
112             if (fileStart < 0) {
113                 message = output;
114             } else {
115                 message = output.substring(msgStart + MSG_KEY.length(), fileStart);
116                 int locStart = output.indexOf(LOCATION_KEY, fileStart + FILE_KEY.length());
117                 if (locStart < 0) {
118                     file = output.substring(fileStart + FILE_KEY.length());
119                 } else {
120                     file = output.substring(fileStart + FILE_KEY.length(), locStart);
121                     int nextLoc = 0;
122                     while(nextLoc >= 0) {
123                         nextLoc = output.indexOf(LOCATION_KEY, locStart + LOCATION_KEY.length());
124                         Location loc;
125                         if (nextLoc < 0) {
126                             loc = Location.fromUserReadableString(
127                                     output.substring(locStart + LOCATION_KEY.length()));
128                         } else {
129                             loc = Location.fromUserReadableString(
130                                     output.substring(locStart + LOCATION_KEY.length(), nextLoc));
131                         }
132                         if (loc != null && loc.isValid()) {
133                             locations.add(loc);
134                         }
135                         locStart = nextLoc;
136                     }
137                 }
138             }
139         }
140         return new ScopedException(message.trim(),
141                 new ScopedErrorReport(Strings.isNullOrEmpty(file) ? null : file.trim(), locations));
142     }
143 
extractErrors(String output)144     public static List<ScopedException> extractErrors(String output) {
145         List<ScopedException> errors = new ArrayList<ScopedException>();
146         int index = output.indexOf(ERROR_LOG_PREFIX);
147         final int limit = output.length();
148         while (index >= 0 && index < limit) {
149             int end = output.indexOf(ERROR_LOG_SUFFIX, index + ERROR_LOG_PREFIX.length());
150             if (end == -1) {
151                 errors.add(createFromOutput(output.substring(index + ERROR_LOG_PREFIX.length())));
152                 break;
153             } else {
154                 errors.add(createFromOutput(output.substring(index + ERROR_LOG_PREFIX.length(),
155                         end)));
156                 index = output.indexOf(ERROR_LOG_PREFIX, end + ERROR_LOG_SUFFIX.length());
157             }
158         }
159         return errors;
160     }
161 
encodeOutput(boolean encodeOutput)162     public static void encodeOutput(boolean encodeOutput) {
163         sEncodeOutput = encodeOutput;
164     }
165 
issEncodeOutput()166     public static boolean issEncodeOutput() {
167         return sEncodeOutput;
168     }
169 }
170