LightJason - AgentSpeak(L++)
CPath.java
Go to the documentation of this file.
1 /*
2  * @cond LICENSE
3  * ######################################################################################
4  * # LGPL License #
5  * # #
6  * # This file is part of the LightJason AgentSpeak(L++) #
7  * # Copyright (c) 2015-19, LightJason (info@lightjason.org) #
8  * # This program is free software: you can redistribute it and/or modify #
9  * # it under the terms of the GNU Lesser General Public License as #
10  * # published by the Free Software Foundation, either version 3 of the #
11  * # License, or (at your option) any later version. #
12  * # #
13  * # This program is distributed in the hope that it will be useful, #
14  * # but WITHOUT ANY WARRANTY; without even the implied warranty of #
15  * # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
16  * # GNU Lesser General Public License for more details. #
17  * # #
18  * # You should have received a copy of the GNU Lesser General Public License #
19  * # along with this program. If not, see http://www.gnu.org/licenses/ #
20  * ######################################################################################
21  * @endcond
22  */
23 
24 package org.lightjason.agentspeak.common;
25 
26 import com.google.common.base.Charsets;
27 import com.google.common.hash.Hasher;
28 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
29 import org.apache.commons.lang3.StringUtils;
31 
32 import javax.annotation.Nonnull;
33 import javax.annotation.Nullable;
34 import java.util.Arrays;
35 import java.util.Collections;
36 import java.util.List;
37 import java.util.Objects;
38 import java.util.Set;
39 import java.util.concurrent.CopyOnWriteArrayList;
40 import java.util.function.BiConsumer;
41 import java.util.function.BinaryOperator;
42 import java.util.function.Function;
43 import java.util.function.Supplier;
44 import java.util.stream.Collector;
45 import java.util.stream.Collectors;
46 import java.util.stream.IntStream;
47 import java.util.stream.Stream;
48 
49 
53 public final class CPath implements IPath
54 {
58  private static final long serialVersionUID = -8502900889333744887L;
62  private final List<String> m_path;
66  private String m_separator = DEFAULTSEPERATOR;
67 
74  public CPath( final IPath p_path, final String... p_varargs )
75  {
76  this( p_path );
77  m_path.addAll( Arrays.asList( p_varargs ) );
78  this.normalize();
79  }
80 
86  public CPath( @Nonnull final IPath p_path )
87  {
88  m_path = p_path.stream().collect( CPath.collectorfactory() );
89  m_separator = p_path.separator();
90  }
91 
97  public CPath( @Nullable final String... p_varargs )
98  {
99  if ( ( Objects.isNull( p_varargs ) ) || ( p_varargs.length == 0 ) )
100  m_path = CPath.listfactory();
101  else
102  {
103  m_path = Arrays.stream( StringUtils.join( p_varargs, m_separator ).split( m_separator ) )
104  .map( String::trim )
105  .filter( i -> !i.isEmpty() )
107  if ( m_path.size() == 0 )
108  throw new CIllegalArgumentException( CCommon.languagestring( this, "pathempty" ) );
109  }
110  this.normalize();
111  }
112 
118  public CPath( @Nonnull final Stream<String> p_stream )
119  {
120  m_path = p_stream.collect( CPath.collectorfactory() );
121  this.normalize();
122  }
123 
127  private CPath()
128  {
129  m_path = Collections.emptyList();
130  }
131 
138  @Nonnull
139  public static IPath createPath( @Nonnull final String... p_varargs )
140  {
141  return new CPath( p_varargs );
142  }
143 
150  @Nonnull
151  public static IPath createPathWithSeperator( @Nonnull final String... p_varargs )
152  {
153  return new CPath(
154  Arrays.asList( p_varargs ).subList( 1, p_varargs.length ).stream()
155  .flatMap( i -> Arrays.stream( StringUtils.split( i, p_varargs[0] ) ) )
156  );
157  }
158 
165  @Nonnull
166  public static IPath from( @Nonnull final String p_string )
167  {
168  return p_string.isEmpty() ? EMPTY : createPathWithSeperator( DEFAULTSEPERATOR, p_string );
169  }
170 
171  @Nonnull
172  @Override
173  public final IPath append( @Nonnull final IPath p_path )
174  {
175  return new CPath( this ).pushback( p_path );
176  }
177 
178  @Nonnull
179  @Override
180  public final IPath append( @Nonnull final String p_path )
181  {
182  return new CPath( this ).pushback( p_path );
183  }
184 
185  @Nonnull
186  @Override
187  public final IPath remove( final int p_index )
188  {
189  if ( !m_path.isEmpty() )
190  m_path.remove( p_index );
191  return this;
192  }
193 
194  @Nonnull
195  @Override
196  public final IPath remove( final int p_start, final int p_end )
197  {
198  m_path.subList( p_start, p_end ).clear();
199  return this;
200  }
201 
202  @Override
203  public final synchronized boolean endswith( @Nonnull final IPath p_path )
204  {
205  return p_path.size() <= this.size()
206  && IntStream.range( 0, p_path.size() ).boxed().parallel().allMatch( i -> this.get( i - p_path.size() ).equals( p_path.get( i ) ) );
207  }
208 
209  @Override
210  public final boolean startswith( @Nonnull final IPath p_path )
211  {
212  return p_path.size() <= this.size()
213  && IntStream.range( 0, p_path.size() ).boxed().parallel().allMatch( i -> this.get( i ).equals( p_path.get( i ) ) );
214 
215  }
216 
217  @Nonnull
218  @Override
219  public final String get( final int p_index )
220  {
221  return p_index < 0 ? m_path.get( m_path.size() + p_index ) : m_path.get( p_index );
222  }
223 
224  @Nonnull
225  @Override
226  public final String path( @Nonnull final String p_separator )
227  {
228  return StringUtils.join( m_path, p_separator );
229  }
230 
231  @Nonnull
232  @Override
233  public final String path()
234  {
235  return StringUtils.join( m_path, m_separator );
236  }
237 
238  @Nonnull
239  @Override
240  public final String separator()
241  {
242  return m_separator;
243  }
244 
245  @Nonnull
246  @Override
247  public final IPath separator( @Nonnull final String p_separator )
248  {
249  if ( p_separator.isEmpty() )
250  throw new CIllegalArgumentException( CCommon.languagestring( this, "separatornotempty" ) );
251 
252  m_separator = p_separator;
253  return this;
254  }
255 
256  @Nonnull
257  @Override
258  public final synchronized IPath lower()
259  {
260  IntStream.range( 0, m_path.size() ).boxed().parallel().forEach( i -> m_path.set( i, m_path.get( i ).toLowerCase() ) );
261  return this;
262  }
263 
264  @Nonnull
265  @Override
266  public final synchronized IPath upper()
267  {
268  IntStream.range( 0, m_path.size() ).boxed().parallel().forEach( i -> m_path.set( i, m_path.get( i ).toUpperCase() ) );
269  return this;
270  }
271 
272  @Nonnull
273  @Override
274  public final IPath subpath( final int p_fromindex )
275  {
276  return this.subpath( p_fromindex, this.size() );
277  }
278 
279  @Nonnull
280  @Override
281  public final IPath subpath( final int p_fromindex, final int p_toindex )
282  {
283  return new CPath(
284  p_toindex == 0
285  ? Stream.empty()
286  : IntStream.range(
287  p_fromindex,
288  p_toindex > 0 ? p_toindex : this.size() + p_toindex
289  )
290  .mapToObj( m_path::get )
291  ).separator( m_separator );
292  }
293 
294  @Nonnull
295  @Override
296  public final synchronized String suffix()
297  {
298  return m_path.isEmpty()
299  ? ""
300  : m_path.get( m_path.size() - 1 );
301  }
302 
303  @Override
304  public final int hashCode()
305  {
306  final Hasher l_hasher = org.lightjason.agentspeak.language.CCommon.getTermHashing();
307  m_path.forEach( i -> l_hasher.putString( i, Charsets.UTF_8 ) );
308  return l_hasher.hash().hashCode();
309  }
310 
311  @Override
312  @SuppressFBWarnings( "EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS" )
313  public final boolean equals( final Object p_object )
314  {
315  return ( ( p_object instanceof IPath ) && ( this.hashCode() == p_object.hashCode() ) )
316  || ( ( p_object instanceof String ) && ( this.path().hashCode() == p_object.hashCode() ) );
317  }
318 
319  @Override
320  public final String toString()
321  {
322  return this.path();
323  }
324 
330  public final boolean empty()
331  {
332  return m_path.isEmpty();
333  }
334 
335  @Nonnull
336  @Override
337  public final IPath pushback( @Nonnull final IPath p_path )
338  {
339  p_path.stream().forEach( m_path::add );
340  return this;
341  }
342 
343  @Nonnull
344  @Override
345  public final IPath pushback( @Nonnull final String p_path )
346  {
347  this.pushback( new CPath( p_path ) );
348  return this;
349  }
350 
351  @Nonnull
352  @Override
353  public final IPath pushfront( @Nonnull final String p_path )
354  {
355  this.pushfront( new CPath( p_path ) );
356  return this;
357  }
358 
359  @Nonnull
360  @Override
361  public final synchronized IPath pushfront( @Nonnull final IPath p_path )
362  {
363  final List<String> l_path = Stream.concat( p_path.stream(), m_path.stream() ).collect( Collectors.toList() );
364  m_path.clear();
365  m_path.addAll( l_path );
366  return this;
367  }
368 
369  @Nonnull
370  @Override
371  public final String removesuffix()
372  {
373  if ( this.empty() )
374  return "";
375 
376  final String l_suffix = this.suffix();
377  if ( m_path.size() > 0 )
378  m_path.remove( m_path.size() - 1 );
379  return l_suffix;
380  }
381 
382  @Nonnull
383  @Override
384  public final IPath reverse()
385  {
386  Collections.reverse( m_path );
387  return this;
388  }
389 
390  @Override
391  public final int size()
392  {
393  return m_path.size();
394  }
395 
396  @Override
397  public final boolean startswith( final String p_path )
398  {
399  return this.startswith( new CPath( p_path ) );
400  }
401 
402  @Nonnull
403  @Override
404  public final Stream<String> stream()
405  {
406  return m_path.stream();
407  }
408 
409  @Override
410  public final int compareTo( @Nonnull final IPath p_path )
411  {
412  return Integer.compare( this.hashCode(), p_path.hashCode() );
413  }
414 
418  private synchronized void normalize()
419  {
420  if ( m_path.isEmpty() )
421  return;
422 
423  // create path-copy and nomalize (remove dot, double-dot and empty values)
424  final List<String> l_dotremove = m_path.stream()
425  .filter( i -> Objects.nonNull( i ) && ( !i.isEmpty() ) && ( !".".equals( i ) ) )
426  .collect( Collectors.toList() );
427  if ( l_dotremove.isEmpty() )
428  return;
429 
430  final String l_last = l_dotremove.get( l_dotremove.size() - 1 );
431  final List<String> l_backremove = IntStream.range( 0, l_dotremove.size() - 1 )
432  .boxed()
433  .filter( i -> !l_dotremove.get( i + 1 ).equals( ".." ) )
434  .map( l_dotremove::get )
435  .collect( Collectors.toList() );
436  if ( !"..".equals( l_last ) )
437  l_backremove.add( l_last );
438 
439  // clear internal path and add optimized path
440  m_path.clear();
441  m_path.addAll( l_backremove );
442  }
443 
449  @Nonnull
450  private static Collector<String, List<String>, List<String>> collectorfactory()
451  {
452  return new Collector<String, List<String>, List<String>>()
453  {
454  @Override
455  public final Supplier<List<String>> supplier()
456  {
457  return CopyOnWriteArrayList<String>::new;
458  }
459 
460  @Override
461  public final BiConsumer<List<String>, String> accumulator()
462  {
463  return List::add;
464  }
465 
466  @Override
467  public final BinaryOperator<List<String>> combiner()
468  {
469  return ( i, j ) ->
470  {
471  i.addAll( j );
472  return i;
473  };
474  }
475 
476  @Override
477  public final Function<List<String>, List<String>> finisher()
478  {
479  return i -> i;
480  }
481 
482  @Override
483  public final Set<Characteristics> characteristics()
484  {
485  return Collections.emptySet();
486  }
487  };
488  }
489 
495  @Nonnull
496  private static List<String> listfactory()
497  {
498  return new CopyOnWriteArrayList<>();
499  }
500 
506  public static Collector<String, IPath, IPath> collect()
507  {
508  return new CPathCollector();
509  }
510 
514  private static final class CPathCollector implements Collector<String, IPath, IPath>
515  {
516 
517  @Override
518  public final Supplier<IPath> supplier()
519  {
520  return () -> new CPath( Stream.empty() );
521  }
522 
523  @Override
524  public final BiConsumer<IPath, String> accumulator()
525  {
526  return IPath::pushback;
527  }
528 
529  @Override
530  public final BinaryOperator<IPath> combiner()
531  {
532  return IPath::pushback;
533  }
534 
535  @Override
536  public final Function<IPath, IPath> finisher()
537  {
538  return Function.identity();
539  }
540 
541  @Override
542  public final Set<Characteristics> characteristics()
543  {
544  return Collections.emptySet();
545  }
546  }
547 }
String DEFAULTSEPERATOR
default seperator
Definition: IPath.java:43
static IPath createPath( @Nonnull final String... p_varargs)
creates a path object from different items
Definition: CPath.java:139
static Collector< String, List< String >, List< String > > collectorfactory()
collector factory
Definition: CPath.java:450
final List< String > m_path
list with path parts *
Definition: CPath.java:62
final BinaryOperator< IPath > combiner()
Definition: CPath.java:530
final int size()
returns the number of path elements
Definition: CPath.java:391
final IPath pushback( @Nonnull final IPath p_path)
adds a path at the end
Definition: CPath.java:337
final synchronized IPath pushfront( @Nonnull final IPath p_path)
adds a path to the front of the path
Definition: CPath.java:361
CPath()
private ctor for empty path
Definition: CPath.java:127
final IPath subpath(final int p_fromindex)
creates a path of the start index until the end
Definition: CPath.java:274
final Stream< String > stream()
stream over elements
Definition: CPath.java:404
static final long serialVersionUID
serial id
Definition: CPath.java:58
final boolean startswith( @Nonnull final IPath p_path)
Definition: CPath.java:210
final int compareTo( @Nonnull final IPath p_path)
Definition: CPath.java:410
common structure for execution definition
final boolean equals(final Object p_object)
Definition: CPath.java:313
final IPath separator( @Nonnull final String p_separator)
sets the separator
Definition: CPath.java:247
static IPath from( @Nonnull final String p_string)
factor method to build path
Definition: CPath.java:166
final synchronized boolean endswith( @Nonnull final IPath p_path)
check of a path ends with another path
Definition: CPath.java:203
CPath(final IPath p_path, final String... p_varargs)
copy-ctor with arguments
Definition: CPath.java:74
static IPath createPathWithSeperator( @Nonnull final String... p_varargs)
creates a path object by splitting a string
Definition: CPath.java:151
static< T > String languagestring(final T p_source, final String p_label, final Object... p_parameter)
returns the language depend string on any object
final boolean empty()
check if the path is empty
Definition: CPath.java:330
class to create a path structure
Definition: CPath.java:53
final synchronized IPath upper()
changes all elements to uppercase
Definition: CPath.java:266
final String path()
returns the full path as string
Definition: CPath.java:233
final IPath subpath(final int p_fromindex, final int p_toindex)
creates a path of the indices
Definition: CPath.java:281
Stream< String > stream()
stream over elements
final Set< Characteristics > characteristics()
Definition: CPath.java:542
final boolean startswith(final String p_path)
check of a path starts with another path
Definition: CPath.java:397
final Function< IPath, IPath > finisher()
Definition: CPath.java:536
static Hasher getTermHashing()
returns the hasing function for term data
static List< String > listfactory()
list factory
Definition: CPath.java:496
final String path( @Nonnull final String p_separator)
Definition: CPath.java:226
final IPath append( @Nonnull final String p_path)
appends a string at the current path and returns the new object
Definition: CPath.java:180
CPath( @Nonnull final Stream< String > p_stream)
ctor
Definition: CPath.java:118
final IPath append( @Nonnull final IPath p_path)
appends a path at the current and returns a new object
Definition: CPath.java:173
final String separator()
returns the separator
Definition: CPath.java:240
synchronized void normalize()
normalize the internal path
Definition: CPath.java:418
final BiConsumer< IPath, String > accumulator()
Definition: CPath.java:524
final synchronized String suffix()
returns the last part of the path
Definition: CPath.java:296
final synchronized IPath lower()
changes all elements to lower-case
Definition: CPath.java:258
String m_separator
separator of the path elements *
Definition: CPath.java:66
IPath pushback( @Nonnull final IPath p_path)
adds a path at the end
final IPath pushfront( @Nonnull final String p_path)
adds a path at the front
Definition: CPath.java:353
CPath( @Nonnull final IPath p_path)
copy-ctor
Definition: CPath.java:86
IPath remove(final int p_index)
removes an element
final IPath pushback( @Nonnull final String p_path)
adds a path at the end
Definition: CPath.java:345
final IPath reverse()
reverse path
Definition: CPath.java:384
CPath( @Nullable final String... p_varargs)
ctor
Definition: CPath.java:97
static Collector< String, IPath, IPath > collect()
returns a collector to build a path from strings
Definition: CPath.java:506
final String removesuffix()
remove the suffix from the path
Definition: CPath.java:371