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