1731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar/*
2731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar * Copyright (C) 2015 The Android Open Source Project
3731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar *
4731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar * you may not use this file except in compliance with the License.
6731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar * You may obtain a copy of the License at
7731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar *
8731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar *
10731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar * Unless required by applicable law or agreed to in writing, software
11731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar * See the License for the specific language governing permissions and
14731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar * limitations under the License.
15731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar */
16731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar
17731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyarpackage android.databinding.tool.processing;
18731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar
194ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta
204ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Guptaimport com.google.common.base.Joiner;
214ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Guptaimport com.google.common.base.Splitter;
224ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Guptaimport com.google.common.base.Strings;
23731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar
24731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyarimport android.databinding.tool.store.Location;
25731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyarimport android.databinding.tool.util.L;
264ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Guptaimport android.databinding.tool.util.StringUtils;
27731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar
28731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyarimport java.util.ArrayList;
29731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyarimport java.util.List;
30731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyarimport java.util.regex.Matcher;
31731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyarimport java.util.regex.Pattern;
32731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar
33731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar/**
34731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar * An exception that contains scope information.
35731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar */
36731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyarpublic class ScopedException extends RuntimeException {
37731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    public static final String ERROR_LOG_PREFIX = "****/ data binding error ****";
38731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    public static final String ERROR_LOG_SUFFIX = "****\\ data binding error ****";
39731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    public static final String MSG_KEY = "msg:";
40731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    public static final String LOCATION_KEY = "loc:";
41731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    public static final String FILE_KEY = "file:";
4208119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar    private static boolean sEncodeOutput = false;
43731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    private ScopedErrorReport mScopedErrorReport;
44731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    private String mScopeLog;
45731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar
46731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    public ScopedException(String message, Object... args) {
47fd8342a51a96282df315cd27055ba539e89a8c9eYigit Boyar        super(message == null ? "unknown data binding exception" :
48fd8342a51a96282df315cd27055ba539e89a8c9eYigit Boyar                args.length == 0 ? message : String.format(message, args));
49731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        mScopedErrorReport = Scope.createReport();
50731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        mScopeLog = L.isDebugEnabled() ? Scope.produceScopeLog() : null;
51731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    }
52731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar
53731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    ScopedException(String message, ScopedErrorReport scopedErrorReport) {
54731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        super(message);
55731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        mScopedErrorReport = scopedErrorReport;
56731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    }
57731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar
58731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    public String getBareMessage() {
59731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        return super.getMessage();
60731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    }
61731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar
62731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    @Override
63731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    public String getMessage() {
6408119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar        return sEncodeOutput ? createEncodedMessage() : createHumanReadableMessage();
6508119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar    }
6608119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar
676047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public String createHumanReadableMessage() {
6808119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar        ScopedErrorReport scopedError = getScopedErrorReport();
6908119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar        StringBuilder sb = new StringBuilder();
7008119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar        sb.append(super.getMessage()).append("\n")
7108119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar                .append("file://").append(scopedError.getFilePath());
7208119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar        if (scopedError.getLocations() != null && scopedError.getLocations().size() > 0) {
7308119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar            sb.append(" Line:");
7408119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar            sb.append(scopedError.getLocations().get(0).startLine);
7508119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar        }
7608119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar        sb.append("\n");
7708119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar        return sb.toString();
7808119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar    }
7908119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar
8008119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar    private String createEncodedMessage() {
81731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        ScopedErrorReport scopedError = getScopedErrorReport();
82731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        StringBuilder sb = new StringBuilder();
83731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        sb.append(ERROR_LOG_PREFIX)
84731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                .append(MSG_KEY).append(super.getMessage()).append("\n")
85731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                .append(FILE_KEY).append(scopedError.getFilePath()).append("\n");
86731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        if (scopedError.getLocations() != null) {
87731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            for (Location location : scopedError.getLocations()) {
88731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                sb.append(LOCATION_KEY).append(location.toUserReadableString()).append("\n");
89731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            }
90731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        }
91731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        sb.append(ERROR_LOG_SUFFIX);
924ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta        return Joiner.on(' ').join(Splitter.on(StringUtils.LINE_SEPARATOR).split(sb));
93731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    }
94731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar
95731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    public ScopedErrorReport getScopedErrorReport() {
96731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        return mScopedErrorReport;
97731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    }
98731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar
99731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    public boolean isValid() {
100731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        return mScopedErrorReport.isValid();
101731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    }
102731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar
103731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    public static ScopedException createFromOutput(String output) {
104731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        String message = "";
105731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        String file = "";
1069784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        List<Location> locations = new ArrayList<Location>();
107731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        int msgStart = output.indexOf(MSG_KEY);
108731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        if (msgStart < 0) {
109731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            message = output;
110731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        } else {
111731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            int fileStart = output.indexOf(FILE_KEY, msgStart + MSG_KEY.length());
112731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            if (fileStart < 0) {
113731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                message = output;
114731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            } else {
115731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                message = output.substring(msgStart + MSG_KEY.length(), fileStart);
116731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                int locStart = output.indexOf(LOCATION_KEY, fileStart + FILE_KEY.length());
117731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                if (locStart < 0) {
118731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                    file = output.substring(fileStart + FILE_KEY.length());
119731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                } else {
120731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                    file = output.substring(fileStart + FILE_KEY.length(), locStart);
121731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                    int nextLoc = 0;
122731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                    while(nextLoc >= 0) {
123731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                        nextLoc = output.indexOf(LOCATION_KEY, locStart + LOCATION_KEY.length());
124731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                        Location loc;
125731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                        if (nextLoc < 0) {
126731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                            loc = Location.fromUserReadableString(
127731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                                    output.substring(locStart + LOCATION_KEY.length()));
128731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                        } else {
129731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                            loc = Location.fromUserReadableString(
130731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                                    output.substring(locStart + LOCATION_KEY.length(), nextLoc));
131731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                        }
132731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                        if (loc != null && loc.isValid()) {
133731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                            locations.add(loc);
134731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                        }
135731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                        locStart = nextLoc;
136731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                    }
137731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                }
138731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            }
139731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        }
140731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        return new ScopedException(message.trim(),
1414ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta                new ScopedErrorReport(Strings.isNullOrEmpty(file) ? null : file.trim(), locations));
142731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    }
143731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar
144731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    public static List<ScopedException> extractErrors(String output) {
1459784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        List<ScopedException> errors = new ArrayList<ScopedException>();
146731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        int index = output.indexOf(ERROR_LOG_PREFIX);
147731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        final int limit = output.length();
148731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        while (index >= 0 && index < limit) {
149731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            int end = output.indexOf(ERROR_LOG_SUFFIX, index + ERROR_LOG_PREFIX.length());
150731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            if (end == -1) {
151731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                errors.add(createFromOutput(output.substring(index + ERROR_LOG_PREFIX.length())));
152731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                break;
153731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            } else {
154731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                errors.add(createFromOutput(output.substring(index + ERROR_LOG_PREFIX.length(),
155731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                        end)));
156731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar                index = output.indexOf(ERROR_LOG_PREFIX, end + ERROR_LOG_SUFFIX.length());
157731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            }
158731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        }
159731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        return errors;
160731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar    }
16108119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar
16208119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar    public static void encodeOutput(boolean encodeOutput) {
16308119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar        sEncodeOutput = encodeOutput;
16408119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar    }
1659784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1669784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public static boolean issEncodeOutput() {
1679784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return sEncodeOutput;
1689784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
169731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar}
170