blob: 4f9eafa81d7e754b7c8a02acf3bda1b373e29d60 [file] [log] [blame]
package com.fasterxml.jackson.core.filter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.math.BigInteger;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.util.JsonGeneratorDelegate;
/**
* Specialized {@link JsonGeneratorDelegate} that allows use of
* {@link TokenFilter} for outputting a subset of content that
* caller tries to generate.
*
* @since 2.6
*/
public class FilteringGeneratorDelegate extends JsonGeneratorDelegate
{
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
/**
* Object consulted to determine whether to write parts of content generator
* is asked to write or not.
*/
protected TokenFilter rootFilter;
/**
* Flag that determines whether filtering will continue after the first
* match is indicated or not: if `false`, output is based on just the first
* full match (returning {@link TokenFilter#INCLUDE_ALL}) and no more
* checks are made; if `true` then filtering will be applied as necessary
* until end of content.
*/
protected boolean _allowMultipleMatches;
/**
* Flag that determines whether path leading up to included content should
* also be automatically included or not. If `false`, no path inclusion is
* done and only explicitly included entries are output; if `true` then
* path from main level down to match is also included as necessary.
*/
protected boolean _includePath;
/* NOTE: this feature is included in the first version (2.6), but
* there is no public API to enable it, yet, since there isn't an
* actual use case. But it seemed possible need could arise, which
* is feature has not yet been removed. If no use is found within
* first version or two, just remove.
*
* Marked as deprecated since its status is uncertain.
*/
@Deprecated
protected boolean _includeImmediateParent;
/*
/**********************************************************
/* Additional state
/**********************************************************
*/
/**
* Although delegate has its own output context it is not sufficient since we actually
* have to keep track of excluded (filtered out) structures as well as ones delegate
* actually outputs.
*/
protected TokenFilterContext _filterContext;
/**
* State that applies to the item within container, used where applicable.
* Specifically used to pass inclusion state between property name and
* property, and also used for array elements.
*/
protected TokenFilter _itemFilter;
/**
* Number of tokens for which {@link TokenFilter#INCLUDE_ALL}
* has been returned
*/
protected int _matchCount;
/*
/**********************************************************
/* Construction, initialization
/**********************************************************
*/
public FilteringGeneratorDelegate(JsonGenerator d, TokenFilter f,
boolean includePath, boolean allowMultipleMatches)
{
// By default, do NOT delegate copy methods
super(d, false);
rootFilter = f;
// and this is the currently active filter for root values
_itemFilter = f;
_filterContext = TokenFilterContext.createRootContext(f);
_includePath = includePath;
_allowMultipleMatches = allowMultipleMatches;
}
/*
/**********************************************************
/* Extended API
/**********************************************************
*/
public TokenFilter getFilter() { return rootFilter; }
public JsonStreamContext getFilterContext() {
return _filterContext;
}
/**
* Accessor for finding number of matches, where specific token and sub-tree
* starting (if structured type) are passed.
*/
public int getMatchCount() {
return _matchCount;
}
/*
/**********************************************************
/* Public API, accessors
/**********************************************************
*/
@Override
public JsonStreamContext getOutputContext() {
/* 11-Apr-2015, tatu: Choice is between pre- and post-filter context;
* let's expose post-filter context that correlates with the view
* of caller.
*/
return _filterContext;
}
/*
/**********************************************************
/* Public API, write methods, structural
/**********************************************************
*/
@Override
public void writeStartArray() throws IOException
{
// First things first: whole-sale skipping easy
if (_itemFilter == null) {
_filterContext = _filterContext.createChildArrayContext(null, false);
return;
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) { // include the whole sub-tree?
_filterContext = _filterContext.createChildArrayContext(_itemFilter, true);
delegate.writeStartArray();
return;
}
// Ok; regular checking state then
_itemFilter = _filterContext.checkValue(_itemFilter);
if (_itemFilter == null) {
_filterContext = _filterContext.createChildArrayContext(null, false);
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
_itemFilter = _itemFilter.filterStartArray();
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
_checkParentPath();
_filterContext = _filterContext.createChildArrayContext(_itemFilter, true);
delegate.writeStartArray();
} else {
_filterContext = _filterContext.createChildArrayContext(_itemFilter, false);
}
}
@SuppressWarnings("deprecation")
@Override
public void writeStartArray(int size) throws IOException
{
if (_itemFilter == null) {
_filterContext = _filterContext.createChildArrayContext(null, false);
return;
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
_filterContext = _filterContext.createChildArrayContext(_itemFilter, true);
delegate.writeStartArray(size);
return;
}
_itemFilter = _filterContext.checkValue(_itemFilter);
if (_itemFilter == null) {
_filterContext = _filterContext.createChildArrayContext(null, false);
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
_itemFilter = _itemFilter.filterStartArray();
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
_checkParentPath();
_filterContext = _filterContext.createChildArrayContext(_itemFilter, true);
delegate.writeStartArray(size);
} else {
_filterContext = _filterContext.createChildArrayContext(_itemFilter, false);
}
}
@Override
public void writeStartArray(Object forValue) throws IOException
{
if (_itemFilter == null) {
_filterContext = _filterContext.createChildArrayContext(null, false);
return;
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
_filterContext = _filterContext.createChildArrayContext(_itemFilter, true);
delegate.writeStartArray(forValue);
return;
}
_itemFilter = _filterContext.checkValue(_itemFilter);
if (_itemFilter == null) {
_filterContext = _filterContext.createChildArrayContext(null, false);
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
_itemFilter = _itemFilter.filterStartArray();
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
_checkParentPath();
_filterContext = _filterContext.createChildArrayContext(_itemFilter, true);
delegate.writeStartArray(forValue);
} else {
_filterContext = _filterContext.createChildArrayContext(_itemFilter, false);
}
}
@Override
public void writeStartArray(Object forValue, int size) throws IOException
{
if (_itemFilter == null) {
_filterContext = _filterContext.createChildArrayContext(null, false);
return;
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
_filterContext = _filterContext.createChildArrayContext(_itemFilter, true);
delegate.writeStartArray(forValue, size);
return;
}
_itemFilter = _filterContext.checkValue(_itemFilter);
if (_itemFilter == null) {
_filterContext = _filterContext.createChildArrayContext(null, false);
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
_itemFilter = _itemFilter.filterStartArray();
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
_checkParentPath();
_filterContext = _filterContext.createChildArrayContext(_itemFilter, true);
delegate.writeStartArray(forValue, size);
} else {
_filterContext = _filterContext.createChildArrayContext(_itemFilter, false);
}
}
@Override
public void writeEndArray() throws IOException
{
_filterContext = _filterContext.closeArray(delegate);
if (_filterContext != null) {
_itemFilter = _filterContext.getFilter();
}
}
@Override
public void writeStartObject() throws IOException
{
if (_itemFilter == null) {
_filterContext = _filterContext.createChildObjectContext(_itemFilter, false);
return;
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
_filterContext = _filterContext.createChildObjectContext(_itemFilter, true);
delegate.writeStartObject();
return;
}
TokenFilter f = _filterContext.checkValue(_itemFilter);
if (f == null) {
return;
}
if (f != TokenFilter.INCLUDE_ALL) {
f = f.filterStartObject();
}
if (f == TokenFilter.INCLUDE_ALL) {
_checkParentPath();
_filterContext = _filterContext.createChildObjectContext(f, true);
delegate.writeStartObject();
} else { // filter out
_filterContext = _filterContext.createChildObjectContext(f, false);
}
}
@Override
public void writeStartObject(Object forValue) throws IOException
{
if (_itemFilter == null) {
_filterContext = _filterContext.createChildObjectContext(_itemFilter, false);
return;
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
_filterContext = _filterContext.createChildObjectContext(_itemFilter, true);
delegate.writeStartObject(forValue);
return;
}
TokenFilter f = _filterContext.checkValue(_itemFilter);
if (f == null) {
return;
}
if (f != TokenFilter.INCLUDE_ALL) {
f = f.filterStartObject();
}
if (f == TokenFilter.INCLUDE_ALL) {
_checkParentPath();
_filterContext = _filterContext.createChildObjectContext(f, true);
delegate.writeStartObject(forValue);
} else { // filter out
_filterContext = _filterContext.createChildObjectContext(f, false);
}
}
@Override
public void writeStartObject(Object forValue, int size) throws IOException
{
if (_itemFilter == null) {
_filterContext = _filterContext.createChildObjectContext(_itemFilter, false);
return;
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
_filterContext = _filterContext.createChildObjectContext(_itemFilter, true);
delegate.writeStartObject(forValue, size);
return;
}
TokenFilter f = _filterContext.checkValue(_itemFilter);
if (f == null) {
return;
}
if (f != TokenFilter.INCLUDE_ALL) {
f = f.filterStartObject();
}
if (f == TokenFilter.INCLUDE_ALL) {
_checkParentPath();
_filterContext = _filterContext.createChildObjectContext(f, true);
delegate.writeStartObject(forValue, size);
} else {
_filterContext = _filterContext.createChildObjectContext(f, false);
}
}
@Override
public void writeEndObject() throws IOException
{
_filterContext = _filterContext.closeObject(delegate);
if (_filterContext != null) {
_itemFilter = _filterContext.getFilter();
}
}
@Override
public void writeFieldName(String name) throws IOException
{
TokenFilter state = _filterContext.setFieldName(name);
if (state == null) {
_itemFilter = null;
return;
}
if (state == TokenFilter.INCLUDE_ALL) {
_itemFilter = state;
delegate.writeFieldName(name);
return;
}
state = state.includeProperty(name);
_itemFilter = state;
if (state == TokenFilter.INCLUDE_ALL) {
_checkPropertyParentPath();
}
}
@Override
public void writeFieldName(SerializableString name) throws IOException
{
TokenFilter state = _filterContext.setFieldName(name.getValue());
if (state == null) {
_itemFilter = null;
return;
}
if (state == TokenFilter.INCLUDE_ALL) {
_itemFilter = state;
delegate.writeFieldName(name);
return;
}
state = state.includeProperty(name.getValue());
_itemFilter = state;
if (state == TokenFilter.INCLUDE_ALL) {
_checkPropertyParentPath();
}
}
// 02-Dec-2019, tatu: Not sure what else to do... so use default impl from base class
@Override
public void writeFieldId(long id) throws IOException {
writeFieldName(Long.toString(id));
}
/*
/**********************************************************
/* Public API, write methods, text/String values
/**********************************************************
*/
@Override
public void writeString(String value) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeString(value)) {
return;
}
}
_checkParentPath();
}
delegate.writeString(value);
}
@Override
public void writeString(char[] text, int offset, int len) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
String value = new String(text, offset, len);
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeString(value)) {
return;
}
}
_checkParentPath();
}
delegate.writeString(text, offset, len);
}
@Override
public void writeString(SerializableString value) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeString(value.getValue())) {
return;
}
}
_checkParentPath();
}
delegate.writeString(value);
}
@Override
public void writeString(Reader reader, int len) throws IOException {
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
// [core#609]: do need to implement, but with 2.10.x TokenFilter no
// useful method to call so will be mostly unfiltered
if (!state.includeString(reader, len)) {
return;
}
}
_checkParentPath();
}
delegate.writeString(reader, len);
}
@Override
public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException
{
if (_checkRawValueWrite()) {
delegate.writeRawUTF8String(text, offset, length);
}
}
@Override
public void writeUTF8String(byte[] text, int offset, int length) throws IOException
{
// not exact match, but best we can do
if (_checkRawValueWrite()) {
delegate.writeUTF8String(text, offset, length);
}
}
/*
/**********************************************************
/* Public API, write methods, binary/raw content
/**********************************************************
*/
@Override
public void writeRaw(String text) throws IOException
{
if (_checkRawValueWrite()) {
delegate.writeRaw(text);
}
}
@Override
public void writeRaw(String text, int offset, int len) throws IOException
{
if (_checkRawValueWrite()) {
delegate.writeRaw(text, offset, len);
}
}
@Override
public void writeRaw(SerializableString text) throws IOException
{
if (_checkRawValueWrite()) {
delegate.writeRaw(text);
}
}
@Override
public void writeRaw(char[] text, int offset, int len) throws IOException
{
if (_checkRawValueWrite()) {
delegate.writeRaw(text, offset, len);
}
}
@Override
public void writeRaw(char c) throws IOException
{
if (_checkRawValueWrite()) {
delegate.writeRaw(c);
}
}
@Override
public void writeRawValue(String text) throws IOException
{
if (_checkRawValueWrite()) {
delegate.writeRawValue(text);
}
}
@Override
public void writeRawValue(String text, int offset, int len) throws IOException
{
if (_checkRawValueWrite()) {
delegate.writeRawValue(text, offset, len);
}
}
@Override
public void writeRawValue(char[] text, int offset, int len) throws IOException
{
if (_checkRawValueWrite()) {
delegate.writeRawValue(text, offset, len);
}
}
@Override
public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) throws IOException
{
if (_checkBinaryWrite()) {
delegate.writeBinary(b64variant, data, offset, len);
}
}
@Override
public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) throws IOException
{
if (_checkBinaryWrite()) {
return delegate.writeBinary(b64variant, data, dataLength);
}
return -1;
}
/*
/**********************************************************
/* Public API, write methods, other value types
/**********************************************************
*/
@Override
public void writeNumber(short v) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeNumber(v)) {
return;
}
}
_checkParentPath();
}
delegate.writeNumber(v);
}
@Override
public void writeNumber(int v) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeNumber(v)) {
return;
}
}
_checkParentPath();
}
delegate.writeNumber(v);
}
@Override
public void writeNumber(long v) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeNumber(v)) {
return;
}
}
_checkParentPath();
}
delegate.writeNumber(v);
}
@Override
public void writeNumber(BigInteger v) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeNumber(v)) {
return;
}
}
_checkParentPath();
}
delegate.writeNumber(v);
}
@Override
public void writeNumber(double v) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeNumber(v)) {
return;
}
}
_checkParentPath();
}
delegate.writeNumber(v);
}
@Override
public void writeNumber(float v) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeNumber(v)) {
return;
}
}
_checkParentPath();
}
delegate.writeNumber(v);
}
@Override
public void writeNumber(BigDecimal v) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeNumber(v)) {
return;
}
}
_checkParentPath();
}
delegate.writeNumber(v);
}
@Override
public void writeNumber(String encodedValue) throws IOException, UnsupportedOperationException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeRawValue()) { // close enough?
return;
}
}
_checkParentPath();
}
delegate.writeNumber(encodedValue);
}
@Override
public void writeNumber(char[] encodedValueBuffer, int offset, int length) throws IOException, UnsupportedOperationException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeRawValue()) { // close enough?
return;
}
}
_checkParentPath();
}
delegate.writeNumber(encodedValueBuffer, offset, length);
}
@Override
public void writeBoolean(boolean v) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeBoolean(v)) {
return;
}
}
_checkParentPath();
}
delegate.writeBoolean(v);
}
@Override
public void writeNull() throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeNull()) {
return;
}
}
_checkParentPath();
}
delegate.writeNull();
}
/*
/**********************************************************
/* Overridden field methods
/**********************************************************
*/
@Override
public void writeOmittedField(String fieldName) throws IOException {
// Hmmh. Not sure how this would work but...
if (_itemFilter != null) {
delegate.writeOmittedField(fieldName);
}
}
/*
/**********************************************************
/* Public API, write methods, Native Ids
/**********************************************************
*/
// 25-Mar-2015, tatu: These are tricky as they sort of predate actual filtering calls.
// Let's try to use current state as a clue at least...
@Override
public void writeObjectId(Object id) throws IOException {
if (_itemFilter != null) {
delegate.writeObjectId(id);
}
}
@Override
public void writeObjectRef(Object id) throws IOException {
if (_itemFilter != null) {
delegate.writeObjectRef(id);
}
}
@Override
public void writeTypeId(Object id) throws IOException {
if (_itemFilter != null) {
delegate.writeTypeId(id);
}
}
/*
/**********************************************************
/* Public API, write methods, serializing Java objects
/**********************************************************
*/
// Base class definitions for these seems correct to me, iff not directly delegating:
/*
@Override
public void writeObject(Object pojo) throws IOException,JsonProcessingException {
if (delegateCopyMethods) {
delegate.writeObject(pojo);
return;
}
// NOTE: copied from
if (pojo == null) {
writeNull();
} else {
if (getCodec() != null) {
getCodec().writeValue(this, pojo);
return;
}
_writeSimpleObject(pojo);
}
}
@Override
public void writeTree(TreeNode rootNode) throws IOException {
if (delegateCopyMethods) {
delegate.writeTree(rootNode);
return;
}
// As with 'writeObject()', we are not check if write would work
if (rootNode == null) {
writeNull();
} else {
if (getCodec() == null) {
throw new IllegalStateException("No ObjectCodec defined");
}
getCodec().writeValue(this, rootNode);
}
}
*/
/*
/**********************************************************
/* Public API, copy-through methods
/**********************************************************
*/
// Base class definitions for these seems correct to me, iff not directly delegating:
/*
@Override
public void copyCurrentEvent(JsonParser jp) throws IOException {
if (delegateCopyMethods) delegate.copyCurrentEvent(jp);
else super.copyCurrentEvent(jp);
}
@Override
public void copyCurrentStructure(JsonParser jp) throws IOException {
if (delegateCopyMethods) delegate.copyCurrentStructure(jp);
else super.copyCurrentStructure(jp);
}
*/
/*
/**********************************************************
/* Helper methods
/**********************************************************
*/
protected void _checkParentPath() throws IOException
{
++_matchCount;
// only need to construct path if parent wasn't written
if (_includePath) {
_filterContext.writePath(delegate);
}
// also: if no multiple matches desired, short-cut checks
if (!_allowMultipleMatches) {
// Mark parents as "skip" so that further check calls are not made
_filterContext.skipParentChecks();
}
}
/**
* Specialized variant of {@link #_checkParentPath} used when checking
* parent for a property name to be included with value: rules are slightly
* different.
*/
protected void _checkPropertyParentPath() throws IOException
{
++_matchCount;
if (_includePath) {
_filterContext.writePath(delegate);
} else if (_includeImmediateParent) {
// 21-Apr-2015, tatu: Note that there is no API to enable this currently...
// retained for speculative future use
_filterContext.writeImmediatePath(delegate);
}
// also: if no multiple matches desired, short-cut checks
if (!_allowMultipleMatches) {
// Mark parents as "skip" so that further check calls are not made
_filterContext.skipParentChecks();
}
}
protected boolean _checkBinaryWrite() throws IOException
{
if (_itemFilter == null) {
return false;
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
return true;
}
if (_itemFilter.includeBinary()) { // close enough?
_checkParentPath();
return true;
}
return false;
}
protected boolean _checkRawValueWrite() throws IOException
{
if (_itemFilter == null) {
return false;
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
return true;
}
if (_itemFilter.includeRawValue()) { // close enough?
_checkParentPath();
return true;
}
return false;
}
}