3
* ====================================================================
4
* Licensed to the Apache Software Foundation (ASF) under one
5
* or more contributor license agreements. See the NOTICE file
6
* distributed with this work for additional information
7
* regarding copyright ownership. The ASF licenses this file
8
* to you under the Apache License, Version 2.0 (the
9
* "License"); you may not use this file except in compliance
10
* with the License. You may obtain a copy of the License at
12
* http://www.apache.org/licenses/LICENSE-2.0
14
* Unless required by applicable law or agreed to in writing,
15
* software distributed under the License is distributed on an
16
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
* KIND, either express or implied. See the License for the
18
* specific language governing permissions and limitations
20
* ====================================================================
24
package org.apache.subversion.javahl.remote;
26
import org.apache.subversion.javahl.types.*;
27
import org.apache.subversion.javahl.callback.*;
29
import org.apache.subversion.javahl.ISVNEditor;
32
import java.util.GregorianCalendar;
33
import java.util.Locale;
34
import java.util.SimpleTimeZone;
35
import java.io.InputStream;
36
import java.io.IOException;
37
import java.nio.charset.Charset;
40
* Package-private editor implementation that converts an editor drive
41
* to {@link RemoteStatus} callbacks.
44
class StatusEditor implements ISVNEditor
46
StatusEditor(RemoteStatus receiver)
48
this.receiver = receiver;
50
protected RemoteStatus receiver = null;
52
protected void checkState()
55
throw new IllegalStateException("Status editor is not active");
60
//DEBUG:System.err.println(" [J] StatusEditor.dispose");
61
if (this.receiver != null)
65
public void addDirectory(String relativePath,
66
Iterable<String> children,
67
Map<String, byte[]> properties,
68
long replacesRevision)
70
//DEBUG:System.err.println(" [J] StatusEditor.addDirectory");
72
receiver.addedDirectory(relativePath);
75
public void addFile(String relativePath,
78
Map<String, byte[]> properties,
79
long replacesRevision)
81
//DEBUG:System.err.println(" [J] StatusEditor.addFile");
82
if (contents != null) {
85
} catch (IOException ex) {
86
throw new RuntimeException(ex);
91
receiver.addedFile(relativePath);
94
public void addSymlink(String relativePath,
96
Map<String, byte[]> properties,
97
long replacesRevision)
99
//DEBUG:System.err.println(" [J] StatusEditor.addSymlink");
101
receiver.addedSymlink(relativePath);
104
public void addAbsent(String relativePath,
106
long replacesRevision)
108
//DEBUG:System.err.println(" [J] StatusEditor.addAbsent");
110
throw new RuntimeException("Not implemented: StatusEditor.addAbsent");
113
public void alterDirectory(String relativePath,
115
Iterable<String> children,
116
Map<String, byte[]> properties)
118
//DEBUG:System.err.println(" [J] StatusEditor.alterDirectory");
120
receiver.modifiedDirectory(relativePath, (children != null),
121
props_changed(properties),
122
make_entry(properties));
125
public void alterFile(String relativePath,
128
InputStream contents,
129
Map<String, byte[]> properties)
131
//DEBUG:System.err.println(" [J] StatusEditor.alterFile");
132
if (contents != null) {
135
} catch (IOException ex) {
136
throw new RuntimeException(ex);
141
receiver.modifiedFile(relativePath,
142
(checksum != null && contents != null),
143
props_changed(properties),
144
make_entry(properties));
147
public void alterSymlink(String relativePath,
150
Map<String, byte[]> properties)
152
//DEBUG:System.err.println(" [J] StatusEditor.alterSymlink");
154
receiver.modifiedSymlink(relativePath, (target != null),
155
props_changed(properties),
156
make_entry(properties));
159
public void delete(String relativePath, long revision)
161
//DEBUG:System.err.println(" [J] StatusEditor.delete");
163
receiver.deleted(relativePath);
166
public void copy(String sourceRelativePath,
168
String destinationRelativePath,
169
long replacesRevision)
171
//DEBUG:System.err.println(" [J] StatusEditor.copy");
173
throw new RuntimeException("Not implemented: StatusEditor.copy");
176
public void move(String sourceRelativePath,
178
String destinationRelativePath,
179
long replacesRevision)
181
//DEBUG:System.err.println(" [J] StatusEditor.move");
183
throw new RuntimeException("Not implemented: StatusEditor.move");
186
public void complete()
188
//DEBUG:System.err.println(" [J] StatusEditor.complete");
194
//DEBUG:System.err.println(" [J] StatusEditor.abort");
200
* Construct a RemoteStatus.Entry record from the given properties.
202
private static final Charset UTF8 = Charset.forName("UTF-8");
203
private static final SimpleTimeZone UTC =
204
new SimpleTimeZone(SimpleTimeZone.UTC_TIME, "UTC");
205
private static final String entryprop_uuid = "svn:entry:uuid";
206
private static final String entryprop_author = "svn:entry:last-author";
207
private static final String entryprop_revision = "svn:entry:committed-rev";
208
private static final String entryprop_timestamp = "svn:entry:committed-date";
209
private final GregorianCalendar entry_calendar =
210
new GregorianCalendar(UTC, Locale.ROOT);
212
// FIXME: Room for improvement here. There are likely to be a lot
213
// of duplicate entries and we should be able to avoid parsing the
214
// duplicates all over again. Need a map <raw data> -> Entry to
215
// just look up the entries instead of parsing them.
216
private final RemoteStatus.Entry make_entry(Map<String, byte[]> properties)
218
final byte[] raw_uuid = properties.get(entryprop_uuid);
219
final byte[] raw_author = properties.get(entryprop_author);
220
final byte[] raw_revision = properties.get(entryprop_revision);
221
final byte[] raw_timestamp = properties.get(entryprop_timestamp);
223
long parsed_timestamp = -1;
224
if (raw_timestamp != null)
226
// Parse: 2013-07-04T23:17:59.128366Z
227
final String isodate = new String(raw_timestamp, UTF8);
229
final int year = Integer.valueOf(isodate.substring(0,4), 10);
230
final int month = Integer.valueOf(isodate.substring(5,7), 10);
231
final int day = Integer.valueOf(isodate.substring(8,10), 10);
232
final int hour = Integer.valueOf(isodate.substring(11,13), 10);
233
final int minute = Integer.valueOf(isodate.substring(14,16), 10);
234
final int second = Integer.valueOf(isodate.substring(17,19), 10);
235
final int micro = Integer.valueOf(isodate.substring(20,26), 10);
236
entry_calendar.set(year, month, day, hour, minute, second);
238
// Use integer rounding to add milliseconds
240
(1000 * entry_calendar.getTimeInMillis() + micro + 500) / 1000;
243
return new RemoteStatus.Entry(
244
(raw_uuid == null ? null : new String(raw_uuid, UTF8)),
245
(raw_author == null ? null : new String(raw_author, UTF8)),
246
(raw_revision == null ? Revision.SVN_INVALID_REVNUM
247
: Long.valueOf(new String(raw_revision, UTF8), 10)),
252
* Filter entry props from the incoming properties
254
private static final String wcprop_prefix = "svn:wc:";
255
private static final String entryprop_prefix = "svn:entry:";
256
private static final boolean props_changed(Map<String, byte[]> properties)
258
if (properties != null)
259
for (String name : properties.keySet())
260
if (!name.startsWith(wcprop_prefix)
261
&& !name.startsWith(entryprop_prefix))