/
Add.java
189 lines (169 loc) · 5.68 KB
/
Add.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
package org.basex.core.cmd;
import static org.basex.core.Text.*;
import java.io.IOException;
import javax.xml.transform.sax.SAXSource;
import org.basex.build.Builder;
import org.basex.build.DirParser;
import org.basex.build.DiskBuilder;
import org.basex.build.MemBuilder;
import org.basex.build.Parser;
import org.basex.build.xml.SAXWrapper;
import org.basex.core.BaseXException;
import org.basex.core.CommandBuilder;
import org.basex.core.Context;
import org.basex.core.User;
import org.basex.data.Data;
import org.basex.io.IO;
import org.basex.io.IOContent;
import org.basex.util.Performance;
import org.basex.util.Util;
import org.xml.sax.InputSource;
/**
* Evaluates the 'add' command and adds a document to a collection.
*
* @author BaseX Team 2005-11, BSD License
* @author Christian Gruen
*/
public final class Add extends ACreate {
/** Builder. */
private Builder build;
/**
* Default constructor.
* @param input input file or XML string
*/
public Add(final String input) {
this(input, null);
}
/**
* Constructor, specifying a document name.
* @param input input file or XML string
* @param name name of document
*/
public Add(final String input, final String name) {
this(input, name, null);
}
/**
* Constructor, specifying a document name and a target.
* @param input input XML file or XML string
* @param name name of document. If {@code null}, the name of the input
* will be used
* @param target target. If {@code null}, target will be set to root
*/
public Add(final String input, final String name, final String target) {
super(DATAREF | User.WRITE, input, name, target == null ? "" : target);
}
@Override
protected boolean run() {
final String input = args[0];
final IO io = IO.get(input);
if(!io.exists()) return error(FILEWHICH, io);
String name = args[1];
if(name != null && !name.isEmpty()) {
// assure that name contains no slashes
if(name.matches(".*[\\\\/].*")) return error(NAMEINVALID, args[1]);
// set specified document name
io.name(name);
} else if(io instanceof IOContent) {
// if no name exists, set database name as document name
name = context.data.meta.name + IO.XMLSUFFIX;
io.name(name);
}
final String trg = path(args[2]);
final DirParser p = new DirParser(io, trg, context.prop);
try {
return info(add(p, context, trg, name, this));
} catch(final BaseXException ex) {
return error(ex.getMessage());
}
}
@Override
public void build(final CommandBuilder cb) {
cb.init().arg(AS, 1).arg(TO, 2).arg(0);
}
/**
* Adds a document to the database.
* @param name name of document. If {@code null}, the name of the input
* will be used
* @param target target. If {@code null}, target will be set to root
* @param input XML input source
* @param ctx database context
* @param cmd calling command
* @param lock if {@code true}, register a write lock in context
* @return info string
* @throws BaseXException database exception
*/
public static String add(final String name, final String target,
final InputSource input, final Context ctx, final Add cmd,
final boolean lock) throws BaseXException {
final Data data = ctx.data;
if(data == null) return PROCNODB;
String trg = path(target);
if(!trg.isEmpty()) trg = trg + '/';
final SAXSource sax = new SAXSource(input);
final Parser parser = new SAXWrapper(sax, name, trg, ctx.prop);
try {
if(lock) ctx.register(true);
return add(parser, ctx, trg, name, cmd);
} finally {
if(lock) ctx.unregister(true);
}
}
/**
* Adds the specified input to the database.
* @param parser parser instance
* @param ctx database context
* @param target target
* @param name name
* @param cmd calling command
* @return info string
* @throws BaseXException database exception
*/
public static String add(final Parser parser, final Context ctx,
final String target, final String name, final Add cmd)
throws BaseXException {
final Performance p = new Performance();
final String path = target + (target.isEmpty() ? "/" : "") +
(name == null ? parser.src.name() : name);
// create disk instances for large documents
// test does not work for input streams and directories
final long fl = parser.src.length();
boolean large = false;
final Runtime rt = Runtime.getRuntime();
if(fl > rt.freeMemory() / 3) {
Performance.gc(2);
large = fl > rt.freeMemory() / 3;
}
// create random database name
final Data data = ctx.data;
final String dbname = large ? data.meta.random() : path;
final Builder build = large ? new DiskBuilder(parser, ctx.prop) :
new MemBuilder(parser, ctx.prop);
if(cmd != null) cmd.build = build;
Data tmp = null;
try {
tmp = build.build(dbname);
// ignore empty fragments
if(tmp.meta.size > 1) {
data.insert(data.meta.size, -1, tmp);
ctx.update();
data.flush();
}
return Util.info(PATHADDED, path, p);
} catch(final IOException ex) {
Util.debug(ex);
throw new BaseXException(ex);
} finally {
// close and drop intermediary database instance
if(tmp != null) try { tmp.close(); } catch(final IOException e) { }
if(large) DropDB.drop(dbname, ctx.prop);
}
}
@Override
protected String tit() {
return BUTTONADD;
}
@Override
protected double prog() {
return build != null ? build.prog() : 0;
}
}