function names = fieldnames (obj)
  if (nargin < 1)
    print_usage ();
  endif
  if (isstruct (obj))
    names = __fieldnames__ (obj);
  elseif (isobject (obj))
    try
      # struct(obj) returns all properties including hidden ones for classdef
      s = struct(obj);
      names = fieldnames(s);
    catch
      try
        names = properties (obj);
      catch
        names = __fieldnames__ (obj);
      end_try_catch
    end_try_catch
    
    # Add dynamic properties
    try
      dyn_names = obj.getDynamicProperties ();
      if (iscell (dyn_names))
        names = [names; dyn_names(:)];
      endif
    catch
    end_try_catch
    names = unique(names, 'stable');
  elseif (isjava (obj) || ischar (obj))
    if (isa (obj, "java.lang.String"))
      obj = class (obj);
    endif
    names_str = javaMethod ("getFields", "org.octave.ClassHelper", obj);
    names = ostrsplit (names_str, ';');
  else
    error ("fieldnames: Invalid input argument");
  endif
endfunction
