001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.fs.shell.find;
019
020import java.io.IOException;
021import java.util.Deque;
022import java.util.LinkedList;
023import java.util.List;
024
025import org.apache.hadoop.conf.Configurable;
026import org.apache.hadoop.conf.Configuration;
027import org.apache.hadoop.fs.FileStatus;
028import org.apache.hadoop.fs.FileSystem;
029import org.apache.hadoop.fs.Path;
030import org.apache.hadoop.fs.shell.PathData;
031
032/**
033 * Abstract expression for use in the
034 * {@link org.apache.hadoop.fs.shell.find.Find} command. Provides default
035 * behavior for a no-argument primary expression.
036 */
037public abstract class BaseExpression implements Expression, Configurable {
038  private String[] usage = { "Not yet implemented" };
039  private String[] help = { "Not yet implemented" };
040
041  /** Sets the usage text for this {@link Expression} */
042  protected void setUsage(String[] usage) {
043    this.usage = usage;
044  }
045
046  /** Sets the help text for this {@link Expression} */
047  protected void setHelp(String[] help) {
048    this.help = help;
049  }
050
051  @Override
052  public String[] getUsage() {
053    return this.usage;
054  }
055
056  @Override
057  public String[] getHelp() {
058    return this.help;
059  }
060
061  @Override
062  public void setOptions(FindOptions options) throws IOException {
063    this.options = options;
064    for (Expression child : getChildren()) {
065      child.setOptions(options);
066    }
067  }
068
069  @Override
070  public void prepare() throws IOException {
071    for (Expression child : getChildren()) {
072      child.prepare();
073    }
074  }
075
076  @Override
077  public void finish() throws IOException {
078    for (Expression child : getChildren()) {
079      child.finish();
080    }
081  }
082
083  /** Options passed in from the {@link Find} command. */
084  private FindOptions options;
085
086  /** Hadoop configuration. */
087  private Configuration conf;
088
089  /** Arguments for this expression. */
090  private LinkedList<String> arguments = new LinkedList<String>();
091
092  /** Children of this expression. */
093  private LinkedList<Expression> children = new LinkedList<Expression>();
094
095  /** Return the options to be used by this expression. */
096  protected FindOptions getOptions() {
097    return (this.options == null) ? new FindOptions() : this.options;
098  }
099
100  @Override
101  public void setConf(Configuration conf) {
102    this.conf = conf;
103  }
104
105  @Override
106  public Configuration getConf() {
107    return this.conf;
108  }
109
110  @Override
111  public String toString() {
112    StringBuilder sb = new StringBuilder();
113    sb.append(getClass().getSimpleName());
114    sb.append("(");
115    boolean firstArg = true;
116    for (String arg : getArguments()) {
117      if (!firstArg) {
118        sb.append(",");
119      } else {
120        firstArg = false;
121      }
122      sb.append(arg);
123    }
124    sb.append(";");
125    firstArg = true;
126    for (Expression child : getChildren()) {
127      if (!firstArg) {
128        sb.append(",");
129      } else {
130        firstArg = false;
131      }
132      sb.append(child.toString());
133    }
134    sb.append(")");
135    return sb.toString();
136  }
137
138  @Override
139  public boolean isAction() {
140    for (Expression child : getChildren()) {
141      if (child.isAction()) {
142        return true;
143      }
144    }
145    return false;
146  }
147
148  @Override
149  public boolean isOperator() {
150    return false;
151  }
152
153  /**
154   * Returns the arguments of this expression
155   *
156   * @return list of argument strings
157   */
158  protected List<String> getArguments() {
159    return this.arguments;
160  }
161
162  /**
163   * Returns the argument at the given position (starting from 1).
164   *
165   * @param position
166   *          argument to be returned
167   * @return requested argument
168   * @throws IOException
169   *           if the argument doesn't exist or is null
170   */
171  protected String getArgument(int position) throws IOException {
172    if (position > this.arguments.size()) {
173      throw new IOException("Missing argument at " + position);
174    }
175    String argument = this.arguments.get(position - 1);
176    if (argument == null) {
177      throw new IOException("Null argument at position " + position);
178    }
179    return argument;
180  }
181
182  /**
183   * Returns the children of this expression.
184   *
185   * @return list of child expressions
186   */
187  protected List<Expression> getChildren() {
188    return this.children;
189  }
190
191  @Override
192  public int getPrecedence() {
193    return 0;
194  }
195
196  @Override
197  public void addChildren(Deque<Expression> exprs) {
198    // no children by default, will be overridden by specific expressions.
199  }
200
201  /**
202   * Add a specific number of children to this expression. The children are
203   * popped off the head of the expressions.
204   *
205   * @param exprs
206   *          deque of expressions from which to take the children
207   * @param count
208   *          number of children to be added
209   */
210  protected void addChildren(Deque<Expression> exprs, int count) {
211    for (int i = 0; i < count; i++) {
212      addChild(exprs.pop());
213    }
214  }
215
216  /**
217   * Add a single argument to this expression. The argument is popped off the
218   * head of the expressions.
219   *
220   * @param expr
221   *          child to add to the expression
222   */
223  private void addChild(Expression expr) {
224    children.push(expr);
225  }
226
227  @Override
228  public void addArguments(Deque<String> args) {
229    // no children by default, will be overridden by specific expressions.
230  }
231
232  /**
233   * Add a specific number of arguments to this expression. The children are
234   * popped off the head of the expressions.
235   *
236   * @param args
237   *          deque of arguments from which to take the argument
238   * @param count
239   *          number of children to be added
240   */
241  protected void addArguments(Deque<String> args, int count) {
242    for (int i = 0; i < count; i++) {
243      addArgument(args.pop());
244    }
245  }
246
247  /**
248   * Add a single argument to this expression. The argument is popped off the
249   * head of the expressions.
250   *
251   * @param arg
252   *          argument to add to the expression
253   */
254  protected void addArgument(String arg) {
255    arguments.add(arg);
256  }
257
258  /**
259   * Returns the {@link FileStatus} from the {@link PathData} item. If the
260   * current options require links to be followed then the returned file status
261   * is that of the linked file.
262   *
263   * @param item
264   *          PathData
265   * @param depth
266   *          current depth in the process directories
267   * @return FileStatus
268   */
269  protected FileStatus getFileStatus(PathData item, int depth)
270      throws IOException {
271    FileStatus fileStatus = item.stat;
272    if (fileStatus.isSymlink()) {
273      if (options.isFollowLink() || (options.isFollowArgLink() &&
274          (depth == 0))) {
275        Path linkedFile = item.fs.resolvePath(fileStatus.getSymlink());
276        fileStatus = getFileSystem(item).getFileStatus(linkedFile);
277      }
278    }
279    return fileStatus;
280  }
281
282  /**
283   * Returns the {@link Path} from the {@link PathData} item.
284   *
285   * @param item
286   *          PathData
287   * @return Path
288   */
289  protected Path getPath(PathData item) throws IOException {
290    return item.path;
291  }
292
293  /**
294   * Returns the {@link FileSystem} associated with the {@link PathData} item.
295   *
296   * @param item PathData
297   * @return FileSystem
298   */
299  protected FileSystem getFileSystem(PathData item) throws IOException {
300    return item.fs;
301  }
302}