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 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 53 ScopedException(String message, ScopedErrorReport scopedErrorReport) { 54 super(message); 55 mScopedErrorReport = scopedErrorReport; 56 } 57 58 public String getBareMessage() { 59 return super.getMessage(); 60 } 61 62 @Override 63 public String getMessage() { 64 return sEncodeOutput ? createEncodedMessage() : createHumanReadableMessage(); 65 } 66 67 private 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 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 95 public ScopedErrorReport getScopedErrorReport() { 96 return mScopedErrorReport; 97 } 98 99 public boolean isValid() { 100 return mScopedErrorReport.isValid(); 101 } 102 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 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 162 public static void encodeOutput(boolean encodeOutput) { 163 sEncodeOutput = encodeOutput; 164 } 165 166 public static boolean issEncodeOutput() { 167 return sEncodeOutput; 168 } 169 } 170