1/*
2 * Copyright (C) 2013 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
17package com.android.documentsui.model;
18
19import android.content.ContentResolver;
20import android.os.Parcel;
21import android.os.Parcelable;
22import android.provider.DocumentsProvider;
23
24import java.io.DataInputStream;
25import java.io.DataOutputStream;
26import java.io.FileNotFoundException;
27import java.io.IOException;
28import java.net.ProtocolException;
29import java.util.Collection;
30import java.util.LinkedList;
31
32/**
33 * Representation of a stack of {@link DocumentInfo}, usually the result of a
34 * user-driven traversal.
35 */
36public class DocumentStack extends LinkedList<DocumentInfo> implements Durable, Parcelable {
37    private static final int VERSION_INIT = 1;
38    private static final int VERSION_ADD_ROOT = 2;
39
40    public RootInfo root;
41
42    public String getTitle() {
43        if (size() == 1 && root != null) {
44            return root.title;
45        } else if (size() > 1) {
46            return peek().displayName;
47        } else {
48            return null;
49        }
50    }
51
52    public boolean isRecents() {
53        return size() == 0;
54    }
55
56    public void updateRoot(Collection<RootInfo> matchingRoots) throws FileNotFoundException {
57        for (RootInfo root : matchingRoots) {
58            if (root.equals(this.root)) {
59                this.root = root;
60                return;
61            }
62        }
63        throw new FileNotFoundException("Failed to find matching root for " + root);
64    }
65
66    /**
67     * Update a possibly stale restored stack against a live
68     * {@link DocumentsProvider}.
69     */
70    public void updateDocuments(ContentResolver resolver) throws FileNotFoundException {
71        for (DocumentInfo info : this) {
72            info.updateSelf(resolver);
73        }
74    }
75
76    /**
77     * Build key that uniquely identifies this stack. It omits most of the raw
78     * details included in {@link #write(DataOutputStream)}, since they change
79     * too regularly to be used as a key.
80     */
81    public String buildKey() {
82        final StringBuilder builder = new StringBuilder();
83        if (root != null) {
84            builder.append(root.authority).append('#');
85            builder.append(root.rootId).append('#');
86        } else {
87            builder.append("[null]").append('#');
88        }
89        for (DocumentInfo doc : this) {
90            builder.append(doc.documentId).append('#');
91        }
92        return builder.toString();
93    }
94
95    @Override
96    public void reset() {
97        clear();
98        root = null;
99    }
100
101    @Override
102    public void read(DataInputStream in) throws IOException {
103        final int version = in.readInt();
104        switch (version) {
105            case VERSION_INIT:
106                throw new ProtocolException("Ignored upgrade");
107            case VERSION_ADD_ROOT:
108                if (in.readBoolean()) {
109                    root = new RootInfo();
110                    root.read(in);
111                }
112                final int size = in.readInt();
113                for (int i = 0; i < size; i++) {
114                    final DocumentInfo doc = new DocumentInfo();
115                    doc.read(in);
116                    add(doc);
117                }
118                break;
119            default:
120                throw new ProtocolException("Unknown version " + version);
121        }
122    }
123
124    @Override
125    public void write(DataOutputStream out) throws IOException {
126        out.writeInt(VERSION_ADD_ROOT);
127        if (root != null) {
128            out.writeBoolean(true);
129            root.write(out);
130        } else {
131            out.writeBoolean(false);
132        }
133        final int size = size();
134        out.writeInt(size);
135        for (int i = 0; i < size; i++) {
136            final DocumentInfo doc = get(i);
137            doc.write(out);
138        }
139    }
140
141    @Override
142    public int describeContents() {
143        return 0;
144    }
145
146    @Override
147    public void writeToParcel(Parcel dest, int flags) {
148        DurableUtils.writeToParcel(dest, this);
149    }
150
151    public static final Creator<DocumentStack> CREATOR = new Creator<DocumentStack>() {
152        @Override
153        public DocumentStack createFromParcel(Parcel in) {
154            final DocumentStack stack = new DocumentStack();
155            DurableUtils.readFromParcel(in, stack);
156            return stack;
157        }
158
159        @Override
160        public DocumentStack[] newArray(int size) {
161            return new DocumentStack[size];
162        }
163    };
164}
165